mirror of
https://github.com/sbt/sbt.git
synced 2026-10-06 10:03:56 +02:00
This provides a small performance improvement during delegation stage of settings processing. This commit doesn't remove the deprecated methods in Project yet. It also removes delegation of the extra axis (currently unused, so no effect on users).
273 lines
11 KiB
Scala
273 lines
11 KiB
Scala
package sbt
|
|
|
|
import Def.{ScopedKey, Setting}
|
|
import Types.{const,idFun,some}
|
|
import complete.Parser
|
|
|
|
import java.io.File
|
|
import java.net.URI
|
|
import org.scalacheck._
|
|
import Prop._
|
|
import Gen._
|
|
import Arbitrary.arbBool
|
|
|
|
// Notes:
|
|
// Generator doesn't produce cross-build project dependencies or do anything with the 'extra' axis
|
|
object TestBuild
|
|
{
|
|
val MaxTasks = 6
|
|
val MaxProjects = 7
|
|
val MaxConfigs = 5
|
|
val MaxBuilds = 4
|
|
val MaxIDSize = 8
|
|
val MaxDeps = 8
|
|
val KeysPerEnv = 10
|
|
|
|
val MaxTasksGen = chooseShrinkable(1, MaxTasks)
|
|
val MaxProjectsGen = chooseShrinkable(1, MaxProjects)
|
|
val MaxConfigsGen = chooseShrinkable(1, MaxConfigs)
|
|
val MaxBuildsGen = chooseShrinkable(1, MaxBuilds)
|
|
val MaxDepsGen = chooseShrinkable(0, MaxDeps)
|
|
|
|
def chooseShrinkable(min: Int, max: Int): Gen[Int] =
|
|
sized( sz => choose(min, (max min sz) max 1) )
|
|
|
|
implicit val cGen = Arbitrary { genConfigs(idGen, MaxDepsGen, MaxConfigsGen) }
|
|
implicit val tGen = Arbitrary { genTasks(idGen, MaxDepsGen, MaxTasksGen) }
|
|
|
|
|
|
final class Keys(val env: Env, val scopes: Seq[Scope])
|
|
{
|
|
override def toString = env + "\n" + scopes.mkString("Scopes:\n\t", "\n\t", "")
|
|
lazy val delegated = scopes map env.delegates
|
|
}
|
|
|
|
final case class Structure(env: Env, current: ProjectRef, data: Settings[Scope], keyIndex: KeyIndex, keyMap: Map[String, AttributeKey[_]])
|
|
{
|
|
override def toString = env.toString + "\n" + "current: " + current + "\nSettings:\n\t" + showData + keyMap.keys.mkString("All keys:\n\t", ", ", "")
|
|
def showKeys(map: AttributeMap): String = map.keys.mkString("\n\t ",",", "\n")
|
|
def showData: String =
|
|
{
|
|
val scopeStrings =
|
|
for( (scope, map) <- data.data ) yield
|
|
(Scope.display(scope, "<key>"), showKeys(map))
|
|
scopeStrings.toSeq.sorted.map(t => t._1 + t._2).mkString("\n\t")
|
|
}
|
|
val extra: BuildUtil[Proj] =
|
|
{
|
|
val getp = (build: URI, project: String) => env.buildMap(build).projectMap(project)
|
|
new BuildUtil(keyIndex, data, env.root.uri, env.rootProject, getp, _.configurations.map(c => ConfigKey(c.name)), Relation.empty)
|
|
}
|
|
|
|
lazy val allAttributeKeys: Set[AttributeKey[_]] = data.data.values.flatMap(_.keys).toSet
|
|
lazy val (taskAxes, globalTaskAxis, onlyTaskAxis, multiTaskAxis) =
|
|
{
|
|
import collection.{breakOut, mutable}
|
|
import mutable.HashSet
|
|
|
|
// task axis of Scope is set to Global and the value of the second map is the original task axis
|
|
val taskAxesMappings =
|
|
for( (scope, keys) <- data.data.toIterable; key <- keys.keys ) yield
|
|
(ScopedKey(scope.copy(task = Global), key), scope.task) : (ScopedKey[_], ScopeAxis[AttributeKey[_]])
|
|
|
|
val taskAxes = Relation.empty ++ taskAxesMappings
|
|
val global = new HashSet[ScopedKey[_]]
|
|
val single = new HashSet[ScopedKey[_]]
|
|
val multi = new HashSet[ScopedKey[_]]
|
|
for( (skey, tasks) <- taskAxes.forwardMap)
|
|
{
|
|
def makeKey(task: ScopeAxis[AttributeKey[_]]) = ScopedKey(skey.scope.copy(task = task), skey.key)
|
|
val hasGlobal = tasks(Global)
|
|
if(hasGlobal)
|
|
global += skey
|
|
else
|
|
{
|
|
val keys = tasks map makeKey
|
|
if( keys.size == 1)
|
|
single ++= keys
|
|
else if(keys.size > 1)
|
|
multi ++= keys
|
|
}
|
|
}
|
|
(taskAxes, global.toSet, single.toSet, multi.toSet)
|
|
}
|
|
}
|
|
final class Env(val builds: Seq[Build], val tasks: Seq[Taskk])
|
|
{
|
|
override def toString = "Env:\n "+ " Tasks:\n " + tasks.mkString("\n ") +"\n" + builds.mkString("\n ")
|
|
val root = builds.head
|
|
val buildMap = mapBy(builds)(_.uri)
|
|
val taskMap = mapBy(tasks)(getKey)
|
|
def project(ref: ProjectRef) = buildMap(ref.build).projectMap(ref.project)
|
|
def projectFor(ref: ResolvedReference) = ref match { case pr: ProjectRef => project(pr); case BuildRef(uri) => buildMap(uri).root }
|
|
|
|
lazy val allProjects = builds.flatMap(_.allProjects)
|
|
def rootProject(uri: URI): String = buildMap(uri).root.id
|
|
def inheritConfig(ref: ResolvedReference, config: ConfigKey) = projectFor(ref).confMap(config.name).extended map toConfigKey
|
|
def inheritTask(task: AttributeKey[_]) = taskMap.get(task) match { case None => Nil; case Some(t) => t.delegates map getKey }
|
|
def resolve(ref: Reference) = Scope.resolveReference(builds.head.uri, rootProject, ref)
|
|
lazy val delegates: Scope => Seq[Scope] =
|
|
Scope.delegates(
|
|
allProjects,
|
|
(_: Proj).configurations.map(toConfigKey),
|
|
resolve,
|
|
uri => buildMap(uri).root.id,
|
|
inheritConfig,
|
|
inheritTask
|
|
)
|
|
lazy val allFullScopes: Seq[Scope] =
|
|
for {
|
|
(ref, p) <- (Global, root.root) +: allProjects.map { case (ref, p) => (Select(ref), p) }
|
|
t <- Global +: tasks.map(t => Select(t.key))
|
|
c <- Global +: p.configurations.map(c => Select(ConfigKey(c.name)))
|
|
} yield
|
|
Scope(project = ref, config = c, task = t, extra = Global)
|
|
}
|
|
def getKey: Taskk => AttributeKey[_] = _.key
|
|
def toConfigKey: Config => ConfigKey = c => ConfigKey(c.name)
|
|
final class Build(val uri: URI, val projects: Seq[Proj])
|
|
{
|
|
override def toString = "Build " + uri.toString + " :\n " + projects.mkString("\n ")
|
|
val allProjects = projects map { p => (ProjectRef(uri, p.id), p) }
|
|
val root = projects.head
|
|
val projectMap = mapBy(projects)(_.id)
|
|
}
|
|
final class Proj(val id: String, val configurations: Seq[Config])
|
|
{
|
|
override def toString = "Project " + id + "\n Configurations:\n " + configurations.mkString("\n ")
|
|
val confMap = mapBy(configurations)(_.name)
|
|
}
|
|
|
|
final class Config(val name: String, val extended: Seq[Config])
|
|
{
|
|
override def toString = name + " (extends: " + extended.map(_.name).mkString(", ") + ")"
|
|
}
|
|
final class Taskk(val key: AttributeKey[String], val delegates: Seq[Taskk])
|
|
{
|
|
override def toString = key.label + " (delegates: " + delegates.map(_.key.label).mkString(", ") + ")"
|
|
}
|
|
|
|
def mapBy[K, T](s: Seq[T])(f: T => K): Map[K, T] = s map { t => (f(t), t) } toMap;
|
|
|
|
implicit lazy val arbKeys: Arbitrary[Keys] = Arbitrary(keysGen)
|
|
lazy val keysGen: Gen[Keys] = for(env <- mkEnv; keyCount <- chooseShrinkable(1, KeysPerEnv); keys <- listOfN(keyCount, scope(env)) ) yield new Keys(env, keys)
|
|
|
|
def scope(env: Env): Gen[Scope] =
|
|
for {
|
|
build <- oneOf(env.builds)
|
|
project <- oneOf(build.projects)
|
|
cAxis <- oneOrGlobal(project.configurations map toConfigKey)
|
|
tAxis <- oneOrGlobal( env.tasks map getKey )
|
|
pAxis <- orGlobal( frequency( (1, BuildRef(build.uri)), (3, ProjectRef(build.uri, project.id) ) ) )
|
|
} yield
|
|
Scope( pAxis, cAxis, tAxis, Global)
|
|
|
|
def orGlobal[T](gen: Gen[T]): Gen[ScopeAxis[T]] =
|
|
frequency( (1, gen map Select.apply), (1, Global) )
|
|
def oneOrGlobal[T](gen: Seq[T]): Gen[ScopeAxis[T]] = orGlobal(oneOf(gen))
|
|
|
|
def makeParser(structure: Structure): Parser[ScopedKey[_]] =
|
|
{
|
|
import structure._
|
|
def confs(uri: URI) = env.buildMap.get(uri).toList.flatMap { _.root.configurations.map(_.name) }
|
|
val defaultConfs: Option[ResolvedReference] => Seq[String] = {
|
|
case None => confs(env.root.uri)
|
|
case Some(BuildRef(uri)) => confs(uri)
|
|
case Some(ref: ProjectRef) => env.project(ref).configurations.map(_.name)
|
|
}
|
|
Act.scopedKey(keyIndex, current, defaultConfs, keyMap, data)
|
|
}
|
|
|
|
def structure(env: Env, settings: Seq[Setting[_]], current: ProjectRef): Structure =
|
|
{
|
|
implicit val display = Def.showRelativeKey(current, env.allProjects.size > 1)
|
|
val data = Def.make(settings)(env.delegates, const(Nil), display)
|
|
val keys = data.allKeys( (s, key) => ScopedKey(s, key))
|
|
val keyMap = keys.map(k => (k.key.label, k.key)).toMap[String, AttributeKey[_]]
|
|
val projectsMap = env.builds.map(b => (b.uri, b.projects.map(_.id).toSet)).toMap
|
|
new Structure(env, current, data, KeyIndex(keys, projectsMap), keyMap)
|
|
}
|
|
|
|
implicit lazy val mkEnv: Gen[Env] =
|
|
{
|
|
implicit val cGen = genConfigs(idGen, MaxDepsGen, MaxConfigsGen)
|
|
implicit val tGen = genTasks(idGen, MaxDepsGen, MaxTasksGen)
|
|
implicit val pGen = (uri: URI) => genProjects(uri)(idGen, MaxProjectsGen, cGen)
|
|
envGen(buildGen(uriGen, pGen), tGen)
|
|
}
|
|
|
|
implicit def maskGen(implicit arbBoolean: Arbitrary[Boolean]): Gen[ScopeMask] =
|
|
{
|
|
val b = arbBoolean.arbitrary
|
|
for(p <- b; c <- b; t <- b; x <- b) yield
|
|
ScopeMask(project = p, config = c, task = t, extra = x)
|
|
}
|
|
|
|
implicit lazy val idGen: Gen[String] = for(size <- chooseShrinkable(1, MaxIDSize); cs <- listOfN(size, alphaChar)) yield cs.mkString
|
|
implicit lazy val optIDGen: Gen[Option[String]] = frequency( (1, idGen map some.fn), (1, None) )
|
|
implicit lazy val uriGen: Gen[URI] = for(sch <- idGen; ssp <- idGen; frag <- optIDGen) yield new URI(sch, ssp, frag.orNull)
|
|
|
|
implicit def envGen(implicit bGen: Gen[Build], tasks: Gen[Seq[Taskk]]): Gen[Env] =
|
|
for(i <- MaxBuildsGen; bs <- listOfN(i, bGen); ts <- tasks) yield new Env(bs, ts)
|
|
implicit def buildGen(implicit uGen: Gen[URI], pGen: URI => Gen[Seq[Proj]]): Gen[Build] = for(u <- uGen; ps <- pGen(u)) yield new Build(u, ps)
|
|
|
|
def nGen[T](igen: Gen[Int])(implicit g: Gen[T]): Gen[List[T]] = igen flatMap { ig => listOfN(ig, g) }
|
|
|
|
implicit def genProjects(build: URI)(implicit genID: Gen[String], count: Gen[Int], confs: Gen[Seq[Config]]): Gen[Seq[Proj]] =
|
|
{
|
|
def projects(size: Int, ids: List[String]): Seq[Gen[Proj]] =
|
|
for(id <- ids) yield
|
|
for(cs <- confs) yield
|
|
new Proj(id, cs)
|
|
for {
|
|
size <- count
|
|
ids <- listOfN(size, genID)
|
|
projs <- sequence(projects(size, ids))
|
|
} yield projs
|
|
}
|
|
|
|
def genConfigs(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Config]] =
|
|
genAcyclicDirect[Config,String](maxDeps, genName, count)( (key, deps) => new Config(key, deps) )
|
|
def genTasks(implicit genName: Gen[String], maxDeps: Gen[Int], count: Gen[Int]): Gen[Seq[Taskk]] =
|
|
genAcyclicDirect[Taskk,String](maxDeps, genName, count)( (key, deps) => new Taskk(AttributeKey[String](key), deps) )
|
|
|
|
def genAcyclicDirect[A,T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])(make: (T, Seq[A]) => A): Gen[Seq[ A ]] =
|
|
genAcyclic[A,T](maxDeps, keyGen, max) { t =>
|
|
Gen.value { deps =>
|
|
make(t, deps)
|
|
}
|
|
}
|
|
|
|
def genAcyclic[A,T](maxDeps: Gen[Int], keyGen: Gen[T], max: Gen[Int])(make: T => Gen[Seq[A] => A]): Gen[Seq[ A ]] =
|
|
max flatMap { count =>
|
|
listOfN(count, keyGen) flatMap { keys =>
|
|
genAcyclic(maxDeps, keys.distinct)(make)
|
|
}
|
|
}
|
|
def genAcyclic[A,T](maxDeps: Gen[Int], keys: List[T])(make: T => Gen[Seq[A] => A]): Gen[Seq[ A ]] =
|
|
genAcyclic(maxDeps, keys, Nil) flatMap { pairs =>
|
|
sequence( pairs.map { case (key, deps) => mapMake(key, deps, make) } ) flatMap { inputs =>
|
|
val made = new collection.mutable.HashMap[T, A]
|
|
for( (key, deps, mk) <- inputs)
|
|
made(key) = mk(deps map made)
|
|
keys map made
|
|
}
|
|
}
|
|
|
|
def mapMake[A,T](key: T, deps: Seq[T], make: T => Gen[Seq[A] => A]): Gen[Inputs[A,T]] =
|
|
make(key) map { (mk: Seq[A] => A) => (key, deps, mk) }
|
|
|
|
def genAcyclic[T](maxDeps: Gen[Int], names: List[T], acc: List[Gen[ (T,Seq[T]) ]]): Gen[Seq[ (T,Seq[T]) ]] =
|
|
names match
|
|
{
|
|
case Nil => sequence(acc)
|
|
case x :: xs =>
|
|
val next = for(depCount <- maxDeps; d <- pick(depCount min xs.size, xs) ) yield (x, d.toList)
|
|
genAcyclic(maxDeps, xs, next :: acc)
|
|
}
|
|
def sequence[T](gs: Seq[Gen[T]]): Gen[Seq[T]] = Gen.parameterized { prms =>
|
|
wrap( gs map { g => g(prms) getOrElse error("failed generator") } )
|
|
}
|
|
type Inputs[A,T] = (T, Seq[T], Seq[A] => A)
|
|
}
|