Merge pull request #3281 from dwijnand/no-eval

Get rid of Eval
This commit is contained in:
eugene yokota 2017-06-28 16:32:31 -04:00 committed by GitHub
commit 25d393bd8b
7 changed files with 443 additions and 432 deletions

View File

@ -2,7 +2,6 @@ package sbt
import sbt.internal.DslEntry
import sbt.librarymanagement.Configuration
import sbt.util.Eval
private[sbt] trait BuildSyntax {
import language.experimental.macros
@ -13,9 +12,8 @@ private[sbt] trait BuildSyntax {
def enablePlugins(ps: AutoPlugin*): DslEntry = DslEntry.DslEnablePlugins(ps)
def disablePlugins(ps: AutoPlugin*): DslEntry = DslEntry.DslDisablePlugins(ps)
def configs(cs: Configuration*): DslEntry = DslEntry.DslConfigs(cs)
def dependsOn(deps: Eval[ClasspathDep[ProjectReference]]*): DslEntry =
DslEntry.DslDependsOn(deps)
def dependsOn(deps: ClasspathDep[ProjectReference]*): DslEntry = DslEntry.DslDependsOn(deps)
// avoid conflict with `sbt.Keys.aggregate`
def aggregateProjects(refs: Eval[ProjectReference]*): DslEntry = DslEntry.DslAggregate(refs)
def aggregateProjects(refs: ProjectReference*): DslEntry = DslEntry.DslAggregate(refs)
}
private[sbt] object BuildSyntax extends BuildSyntax

View File

@ -34,7 +34,7 @@ import sbt.internal.util.{ AttributeKey, AttributeMap, Dag, Relation, Settings,
import sbt.internal.util.Types.{ const, idFun }
import sbt.internal.util.complete.DefaultParsers
import sbt.librarymanagement.Configuration
import sbt.util.{ Eval, Show }
import sbt.util.Show
import sjsonnew.JsonFormat
import language.experimental.macros
@ -109,28 +109,21 @@ sealed trait ProjectDefinition[PR <: ProjectReference] {
}
sealed trait Project extends ProjectDefinition[ProjectReference] {
private[sbt] def settingsEval: Eval[Seq[Def.Setting[_]]]
private[sbt] def aggregateEval: Eval[Seq[ProjectReference]]
private[sbt] def delegatesEval: Eval[Seq[ProjectReference]]
private[sbt] def dependenciesEval: Eval[Seq[ClasspathDep[ProjectReference]]]
// TODO: add parameters for plugins in 0.14.0 (not reasonable to do in a binary compatible way in 0.13)
private[sbt] def copy(
id: String = id,
base: File = base,
aggregateEval: Eval[Seq[ProjectReference]] = aggregateEval,
dependenciesEval: Eval[Seq[ClasspathDep[ProjectReference]]] = dependenciesEval,
delegatesEval: Eval[Seq[ProjectReference]] = delegatesEval,
settingsEval: Eval[Seq[Setting[_]]] = settingsEval,
aggregate: Seq[ProjectReference] = aggregate,
dependencies: Seq[ClasspathDep[ProjectReference]] = dependencies,
settings: Seq[Setting[_]] = settings,
configurations: Seq[Configuration] = configurations
): Project =
unresolved(
id,
base,
aggregateEval = aggregateEval,
dependenciesEval = dependenciesEval,
delegatesEval = delegatesEval,
settingsEval = settingsEval,
aggregate = aggregate,
dependencies = dependencies,
settings = settings,
configurations,
plugins,
autoPlugins,
@ -145,10 +138,9 @@ sealed trait Project extends ProjectDefinition[ProjectReference] {
resolved(
id,
base,
aggregateEval = aggregateEval map resolveRefs,
dependenciesEval = dependenciesEval map resolveDeps,
delegatesEval = delegatesEval map resolveRefs,
settingsEval,
aggregate = resolveRefs(aggregate),
dependencies = resolveDeps(dependencies),
settings,
configurations,
plugins,
autoPlugins,
@ -164,10 +156,9 @@ sealed trait Project extends ProjectDefinition[ProjectReference] {
unresolved(
id,
base,
aggregateEval = aggregateEval map resolveRefs,
dependenciesEval = dependenciesEval map resolveDeps,
delegatesEval = delegatesEval map resolveRefs,
settingsEval,
aggregate = resolveRefs(aggregate),
dependencies = resolveDeps(dependencies),
settings,
configurations,
plugins,
autoPlugins,
@ -200,57 +191,19 @@ sealed trait Project extends ProjectDefinition[ProjectReference] {
def configs(cs: Configuration*): Project = copy(configurations = configurations ++ cs)
/** Adds classpath dependencies on internal or external projects. */
def dependsOn(deps: Eval[ClasspathDep[ProjectReference]]*): Project =
copy(dependenciesEval = dependenciesEval flatMap { ds0 =>
sequenceEval(deps.toSeq) map { ds1 =>
ds0 ++ ds1
}
})
/** Adds classpath dependencies on internal or external projects. */
def dependsOnSeq(deps: => Seq[ClasspathDep[ProjectReference]]): Project =
copy(dependenciesEval = dependenciesEval flatMap { ds0 =>
Eval.later { deps } map { ds1 =>
ds0 ++ ds1
}
})
def dependsOn(deps: ClasspathDep[ProjectReference]*): Project =
copy(dependencies = dependencies ++ deps)
/**
* Adds projects to be aggregated. When a user requests a task to run on this project from the command line,
* the task will also be run in aggregated projects.
*/
def aggregate(refs: Eval[ProjectReference]*): Project =
copy(aggregateEval = aggregateEval flatMap { as0 =>
sequenceEval(refs.toSeq) map { as1 =>
as0 ++ as1
}
})
/**
* Adds projects to be aggregated. When a user requests a task to run on this project from the command line,
* the task will also be run in aggregated projects.
*/
def aggregateSeq(refs: => Seq[ProjectReference]): Project =
copy(aggregateEval = aggregateEval flatMap { as0 =>
// sequenceEval(refs.toSeq) map { as1 => as0 ++ as1 }
Eval.later { refs } map { as1 =>
as0 ++ as1
}
})
def aggregate(refs: ProjectReference*): Project =
copy(aggregate = (aggregate: Seq[ProjectReference]) ++ refs)
/** Appends settings to the current settings sequence for this project. */
def settings(ss: Def.SettingsDefinition*): Project =
copy(settingsEval = settingsEval map { ss0 =>
(ss0: Seq[Def.Setting[_]]) ++ Def.settings(ss: _*)
})
/** Appends settings to the current settings sequence for this project. */
def settingsLazy(ss: Eval[Def.SettingsDefinition]*): Project =
copy(settingsEval = settingsEval flatMap { ss0 =>
sequenceEval(ss.toSeq) map { ss1 =>
(ss0: Seq[Def.Setting[_]]) ++ Def.settings(ss1: _*)
}
})
copy(settings = (settings: Seq[Def.Setting[_]]) ++ Def.settings(ss: _*))
/**
* Sets the [[AutoPlugin]]s of this project.
@ -267,10 +220,9 @@ sealed trait Project extends ProjectDefinition[ProjectReference] {
unresolved(
id,
base,
aggregateEval = aggregateEval,
dependenciesEval = dependenciesEval,
delegatesEval = delegatesEval,
settingsEval,
aggregate = aggregate,
dependencies = dependencies,
settings,
configurations,
ns,
autoPlugins,
@ -284,10 +236,9 @@ sealed trait Project extends ProjectDefinition[ProjectReference] {
unresolved(
id,
base,
aggregateEval = aggregateEval,
dependenciesEval = dependenciesEval,
delegatesEval = delegatesEval,
settingsEval,
aggregate = aggregate,
dependencies = dependencies,
settings,
configurations,
plugins,
autos,
@ -301,10 +252,9 @@ sealed trait Project extends ProjectDefinition[ProjectReference] {
unresolved(
id,
base,
aggregateEval = aggregateEval,
dependenciesEval = dependenciesEval,
delegatesEval = delegatesEval,
settingsEval,
aggregate = aggregate,
dependencies = dependencies,
settings,
configurations,
plugins,
autoPlugins,
@ -317,13 +267,16 @@ sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] {
/** The [[AutoPlugin]]s enabled for this project as computed from [[plugins]].*/
def autoPlugins: Seq[AutoPlugin]
}
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]
@ -332,45 +285,29 @@ object Project extends ProjectExtra {
private abstract class ProjectDef[PR <: ProjectReference](
val id: String,
val base: File,
val aggregateEval: Eval[Seq[PR]],
val dependenciesEval: Eval[Seq[ClasspathDep[PR]]],
val delegatesEval: Eval[Seq[PR]],
val settingsEval: Eval[Seq[Def.Setting[_]]],
val aggregate: Seq[PR],
val dependencies: Seq[ClasspathDep[PR]],
val settings: Seq[Def.Setting[_]],
val configurations: Seq[Configuration],
val plugins: Plugins,
val autoPlugins: Seq[AutoPlugin],
val projectOrigin: ProjectOrigin
) extends ProjectDefinition[PR] {
def aggregate: Seq[PR] = aggregateEval.value
def dependencies: Seq[ClasspathDep[PR]] = dependenciesEval.value
def delegates: Seq[PR] = delegatesEval.value
def settings: Seq[Def.Setting[_]] = settingsEval.value
Dag.topologicalSort(configurations)(_.extendsConfigs) // checks for cyclic references here instead of having to do it in Scope.delegates
}
private def evalNil[A]: Eval[Seq[A]] = Eval.now(Vector())
// hardcoded version of sequence for flipping Eval and Seq
def sequenceEval[A](es: Seq[Eval[A]]): Eval[Seq[A]] =
(evalNil[A] /: es) { (acc0, x0) =>
acc0 flatMap { acc =>
x0 map { x =>
acc :+ x
}
}
}
def apply(id: String, base: File): Project =
unresolved(id,
base,
evalNil,
evalNil,
evalNil,
evalNil,
Nil,
Plugins.empty,
Nil,
ProjectOrigin.Organic)
unresolved(
id,
base,
Nil,
Nil,
Nil,
Nil,
Plugins.empty,
Nil,
ProjectOrigin.Organic
)
// TODO: add parameter for plugins and projectOrigin in 1.0
// TODO: Modify default settings to be the core settings, and automatically add the IvyModule + JvmPlugins.
@ -385,38 +322,47 @@ object Project extends ProjectExtra {
if (isProjectLoaded(state)) showContextKey(session(state), structure(state), keyNameColor)
else Def.showFullKey
def showContextKey(session: SessionSettings,
structure: BuildStructure,
keyNameColor: Option[String] = None): Show[ScopedKey[_]] =
def showContextKey(
session: SessionSettings,
structure: BuildStructure,
keyNameColor: Option[String] = None
): Show[ScopedKey[_]] =
Def.showRelativeKey(session.current, structure.allProjects.size > 1, keyNameColor)
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)
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
)
/** This is a variation of def apply that mixes in GeneratedRootProject. */
private[sbt] def mkGeneratedRoot(id: String,
base: File,
aggregate: Eval[Seq[ProjectReference]]): Project = {
validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
new ProjectDef[ProjectReference](id,
base,
aggregate,
evalNil,
evalNil,
evalNil,
Nil,
Plugins.empty,
Nil,
ProjectOrigin.GenericRoot) with Project
with GeneratedRootProject
private[sbt] def mkGeneratedRoot(
id: String,
base: File,
aggregate: Seq[ProjectReference]
): Project = {
validProjectID(id).foreach(errMsg => sys.error(s"Invalid project ID: $errMsg"))
new ProjectDef[ProjectReference](
id,
base,
aggregate,
Nil,
Nil,
Nil,
Plugins.empty,
Nil,
ProjectOrigin.GenericRoot
) with Project with GeneratedRootProject
}
/** Returns None if `id` is a valid Project ID or Some containing the parser error message if it is not.*/
def validProjectID(id: String): Option[String] =
DefaultParsers.parse(id, DefaultParsers.ID).left.toOption
private[this] def validProjectIDStart(id: String): Boolean =
DefaultParsers.parse(id, DefaultParsers.IDStart).isRight
@ -438,66 +384,74 @@ object Project extends ProjectExtra {
*/
def normalizeModuleID(id: String): String = normalizeBase(id)
private def resolved(id: String,
base: File,
aggregateEval: Eval[Seq[ProjectRef]],
dependenciesEval: Eval[Seq[ClasspathDep[ProjectRef]]],
delegatesEval: Eval[Seq[ProjectRef]],
settingsEval: Eval[Seq[Def.Setting[_]]],
configurations: Seq[Configuration],
plugins: Plugins,
autoPlugins: Seq[AutoPlugin],
origin: ProjectOrigin): ResolvedProject =
new ProjectDef[ProjectRef](id,
base,
aggregateEval,
dependenciesEval,
delegatesEval,
settingsEval,
configurations,
plugins,
autoPlugins,
origin) with ResolvedProject
private def resolved(
id: String,
base: File,
aggregate: Seq[ProjectRef],
dependencies: Seq[ClasspathDep[ProjectRef]],
settings: Seq[Def.Setting[_]],
configurations: Seq[Configuration],
plugins: Plugins,
autoPlugins: Seq[AutoPlugin],
origin: ProjectOrigin
): ResolvedProject =
new ProjectDef[ProjectRef](
id,
base,
aggregate,
dependencies,
settings,
configurations,
plugins,
autoPlugins,
origin
) with ResolvedProject
private def unresolved(id: String,
base: File,
aggregateEval: Eval[Seq[ProjectReference]],
dependenciesEval: Eval[Seq[ClasspathDep[ProjectReference]]],
delegatesEval: Eval[Seq[ProjectReference]],
settingsEval: Eval[Seq[Def.Setting[_]]],
configurations: Seq[Configuration],
plugins: Plugins,
autoPlugins: Seq[AutoPlugin],
origin: ProjectOrigin): Project = {
private def unresolved(
id: String,
base: File,
aggregate: Seq[ProjectReference],
dependencies: Seq[ClasspathDep[ProjectReference]],
settings: Seq[Def.Setting[_]],
configurations: Seq[Configuration],
plugins: Plugins,
autoPlugins: Seq[AutoPlugin],
origin: ProjectOrigin
): Project = {
validProjectID(id).foreach(errMsg => sys.error("Invalid project ID: " + errMsg))
new ProjectDef[ProjectReference](id,
base,
aggregateEval,
dependenciesEval,
delegatesEval,
settingsEval,
configurations,
plugins,
autoPlugins,
origin) with Project
new ProjectDef[ProjectReference](
id,
base,
aggregate,
dependencies,
settings,
configurations,
plugins,
autoPlugins,
origin
) with Project
}
final class Constructor(p: ProjectReference) {
def %(conf: Configuration): ClasspathDependency = %(conf.name)
def %(conf: String): ClasspathDependency = new ClasspathDependency(p, Some(conf))
def %(conf: String): ClasspathDependency = ClasspathDependency(p, Some(conf))
}
def getOrError[T](state: State, key: AttributeKey[T], msg: String): T =
state get key getOrElse sys.error(msg)
def structure(state: State): BuildStructure =
getOrError(state, stateBuildStructure, "No build loaded.")
def session(state: State): SessionSettings =
getOrError(state, sessionSettings, "Session not initialized.")
def isProjectLoaded(state: State): Boolean =
(state has sessionSettings) && (state has stateBuildStructure)
def extract(state: State): Extracted = extract(session(state), structure(state))
private[sbt] def extract(se: SessionSettings, st: BuildStructure): Extracted =
Extracted(st, se, se.current)(showContextKey(se, st))
@ -506,6 +460,7 @@ object Project extends ProjectExtra {
def getProject(ref: ProjectRef, structure: BuildStructure): Option[ResolvedProject] =
getProject(ref, structure.units)
def getProject(ref: ProjectRef, structure: LoadedBuild): Option[ResolvedProject] =
getProject(ref, structure.units)
@ -531,8 +486,10 @@ object Project extends ProjectExtra {
}
def orIdentity[T](opt: Option[T => T]): T => T = opt getOrElse idFun
def getHook[T](key: SettingKey[T => T], data: Settings[Scope]): T => T =
orIdentity(key in Global get data)
def getHooks(data: Settings[Scope]): (State => State, State => State) =
(getHook(Keys.onLoad, data), getHook(Keys.onUnload, data))
@ -561,8 +518,10 @@ object Project extends ProjectExtra {
val newAttrs = setCond(serverPort.key, port, newAttrs0)
.put(historyPath.key, history)
.put(templateResolverInfos.key, trs)
s.copy(attributes = setCond(shellPrompt.key, prompt, newAttrs),
definedCommands = newDefinedCommands)
s.copy(
attributes = setCond(shellPrompt.key, prompt, newAttrs),
definedCommands = newDefinedCommands
)
}
def setCond[T](key: AttributeKey[T], vopt: Option[T], attributes: AttributeMap): AttributeMap =
@ -792,7 +751,7 @@ object Project extends ProjectExtra {
case Current =>
val base = s.configuration.baseDirectory
projectReturn(s) match {
case Nil => (setProjectReturn(s, base :: Nil), base); case x :: xs => (s, x)
case Nil => (setProjectReturn(s, base :: Nil), base); case x :: _ => (s, x)
}
case Plugins =>
@ -805,9 +764,11 @@ object Project extends ProjectExtra {
(newS, newBase)
}
def runTask[T](taskKey: ScopedKey[Task[T]],
state: State,
checkCycles: Boolean = false): Option[(State, Result[T])] = {
def runTask[T](
taskKey: ScopedKey[Task[T]],
state: State,
checkCycles: Boolean = false
): Option[(State, Result[T])] = {
val extracted = Project.extract(state)
val ch = EvaluateTask.cancelStrategy(extracted, extracted.structure, state)
val p = EvaluateTask.executeProgress(extracted, extracted.structure, state)
@ -817,9 +778,11 @@ object Project extends ProjectExtra {
runTask(taskKey, state, EvaluateTaskConfig(r, checkCycles, p, ch, fgc, mfi))
}
def runTask[T](taskKey: ScopedKey[Task[T]],
state: State,
config: EvaluateTaskConfig): Option[(State, Result[T])] = {
def runTask[T](
taskKey: ScopedKey[Task[T]],
state: State,
config: EvaluateTaskConfig
): Option[(State, Result[T])] = {
val extracted = Project.extract(state)
EvaluateTask(extracted.structure, taskKey, state, extracted.currentRef, config)
}
@ -862,35 +825,14 @@ object Project extends ProjectExtra {
private[sbt] trait GeneratedRootProject
trait ProjectExtra0 {
implicit def wrapProjectReferenceSeqEval[T](rs: => Seq[T])(
implicit ev: T => ProjectReference): Seq[Eval[ProjectReference]] =
rs map (r => Eval.later(r: ProjectReference))
}
trait ProjectExtra extends ProjectExtra0 {
implicit def classpathDependencyEval[T](p: => T)(
implicit ev: T => ClasspathDep[ProjectReference]): Eval[ClasspathDep[ProjectReference]] =
Eval.later(p: ClasspathDep[ProjectReference])
implicit def wrapProjectReferenceEval[T](ref: => T)(
implicit ev: T => ProjectReference): Eval[ProjectReference] =
Eval.later(ref: ProjectReference)
implicit def wrapSettingDefinitionEval[T](d: => T)(
implicit ev: T => Def.SettingsDefinition): Eval[Def.SettingsDefinition] =
Eval.later(d)
implicit def wrapSettingSeqEval(ss: => Seq[Setting[_]]): Eval[Def.SettingsDefinition] =
Eval.later(new Def.SettingList(ss))
trait ProjectExtra {
implicit def configDependencyConstructor[T](p: T)(
implicit ev: T => ProjectReference): Constructor =
new Constructor(p)
implicit def classpathDependency[T](p: T)(
implicit ev: T => ProjectReference): ClasspathDep[ProjectReference] =
ClasspathDependency(p, None)
implicit def classpathDependency[T](
p: T
)(implicit ev: T => ProjectReference): ClasspathDependency = 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

View File

@ -10,8 +10,6 @@ import Def.Setting
import sbt.io.Hash
import sbt.internal.util.Attributed
import sbt.internal.inc.ReflectUtilities
import sbt.util.Eval
import sbt.Project._
trait BuildDef {
def projectDefinitions(baseDirectory: File): Seq[Project] = projects
@ -29,9 +27,11 @@ trait BuildDef {
private[sbt] object BuildDef {
val defaultEmpty: BuildDef = new BuildDef { override def projects = Nil }
val default: BuildDef = new BuildDef {
override def projectDefinitions(base: File) = defaultProject(defaultID(base), base) :: Nil
}
def defaultAggregated(id: String, aggregate: Seq[ProjectRef]): BuildDef = new BuildDef {
override def projectDefinitions(base: File) =
defaultAggregatedProject(id, base, aggregate) :: Nil
@ -42,13 +42,17 @@ private[sbt] object BuildDef {
def defaultProject(id: String, base: File): Project =
Project(id, base).settings(defaultProjectSettings)
def defaultAggregatedProject(id: String, base: File, agg: Seq[ProjectRef]): Project =
defaultProject(id, base).aggregate(agg: _*)
private[sbt] def generatedRootWithoutIvyPlugin(id: String,
base: File,
agg: Seq[ProjectRef]): Project =
Project.mkGeneratedRoot(id, base, Eval.later(agg)).settings(defaultProjectSettings)
private[sbt] def generatedRootWithoutIvyPlugin(
id: String,
base: File,
agg: Seq[ProjectRef]
): Project =
Project.mkGeneratedRoot(id, base, agg).settings(defaultProjectSettings)
private[sbt] def defaultProjectSettings: Seq[Setting[_]] = Seq(
// TODO - Can we move this somewhere else? ordering of settings is causing this to get borked.
// if the user has overridden the name, use the normal organization that is derived from the name.
@ -61,7 +65,8 @@ private[sbt] object BuildDef {
},
autoGeneratedProject := true
)
def analyzed(in: Seq[Attributed[_]]): Seq[xsbti.compile.CompileAnalysis] = in.flatMap {
_.metadata.get(Keys.analysis)
}
def analyzed(in: Seq[Attributed[_]]): Seq[xsbti.compile.CompileAnalysis] =
in.flatMap { _.metadata.get(Keys.analysis) }
}

View File

@ -2,7 +2,6 @@ package sbt
package internal
import sbt.internal.util.RangePosition
import sbt.util.Eval
import sbt.librarymanagement.Configuration
@ -64,8 +63,7 @@ object DslEntry {
}
/** Represents registering an internal dependency for the current project */
case class DslDependsOn(cs: Seq[Eval[ClasspathDep[ProjectReference]]])
extends ProjectManipulation {
case class DslDependsOn(cs: Seq[ClasspathDep[ProjectReference]]) extends ProjectManipulation {
override val toFunction: Project => Project = _.dependsOn(cs: _*)
}
@ -75,7 +73,7 @@ object DslEntry {
}
/** this represents an `aggregate()` in the sbt DSL */
case class DslAggregate(refs: Seq[Eval[ProjectReference]]) extends ProjectManipulation {
case class DslAggregate(refs: Seq[ProjectReference]) extends ProjectManipulation {
override val toFunction: Project => Project = _.aggregate(refs: _*)
}
}

View File

@ -38,7 +38,7 @@ import sbt.internal.util.Types.const
import sbt.internal.util.{ Attributed, Settings, ~> }
import sbt.io.{ GlobFilter, IO, Path }
import sbt.librarymanagement.{ Configuration, Configurations, Resolver, UpdateOptions }
import sbt.util.{ Eval => Ev, Show, Logger }
import sbt.util.{ Show, Logger }
import scala.annotation.tailrec
import scala.tools.nsc.reporters.ConsoleReporter
import Scope.GlobalScope
@ -46,11 +46,13 @@ import xsbti.compile.{ ClasspathOptionsUtil, Compilers }
private[sbt] 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) = {
def defaultLoad(
state: State,
baseDirectory: File,
log: Logger,
isPlugin: Boolean = false,
topLevelExtras: List[URI] = Nil
): (() => Eval, BuildStructure) = {
val (base, config) = timed("Load.defaultLoad until apply", log) {
val globalBase = getGlobalBase(state)
val base = baseDirectory.getCanonicalFile
@ -64,10 +66,12 @@ private[sbt] object Load {
result
}
def defaultPreGlobal(state: State,
baseDirectory: File,
globalBase: File,
log: Logger): LoadBuildConfiguration = {
def defaultPreGlobal(
state: State,
baseDirectory: File,
globalBase: File,
log: Logger
): LoadBuildConfiguration = {
val app = state.configuration
val provider = app.provider
val scalaProvider = app.provider.scalaProvider
@ -101,26 +105,30 @@ private[sbt] object Load {
scalaJarsTarget = zincDir,
log = log
)
val compilers = ZincUtil.compilers(instance = si,
classpathOptions = classpathOptions,
javaHome = None,
scalac)
val compilers = ZincUtil.compilers(
instance = si,
classpathOptions = classpathOptions,
javaHome = None,
scalac
)
val evalPluginDef = EvaluateTask.evalPluginDef(log) _
val delegates = defaultDelegates
val pluginMgmt = PluginManagement(loader)
val inject = InjectSettings(injectGlobal(state), Nil, const(Nil))
LoadBuildConfiguration(stagingDirectory,
classpath,
loader,
compilers,
evalPluginDef,
delegates,
EvaluateTask.injectStreams,
pluginMgmt,
inject,
None,
Nil,
log)
LoadBuildConfiguration(
stagingDirectory,
classpath,
loader,
compilers,
evalPluginDef,
delegates,
EvaluateTask.injectStreams,
pluginMgmt,
inject,
None,
Nil,
log
)
}
private def bootIvyHome(app: xsbti.AppConfiguration): Option[File] =
@ -134,34 +142,40 @@ private[sbt] object Load {
DefaultBackgroundJobService.backgroundJobServiceSetting +:
EvaluateTask.injectSettings
def defaultWithGlobal(state: State,
base: File,
rawConfig: LoadBuildConfiguration,
globalBase: File,
log: Logger): LoadBuildConfiguration = {
def defaultWithGlobal(
state: State,
base: File,
rawConfig: LoadBuildConfiguration,
globalBase: File,
log: Logger
): LoadBuildConfiguration = {
val globalPluginsDir = getGlobalPluginsDirectory(state, globalBase)
val withGlobal = loadGlobal(state, base, globalPluginsDir, rawConfig)
val globalSettings = configurationSources(getGlobalSettingsDirectory(state, globalBase))
loadGlobalSettings(base, globalBase, globalSettings, withGlobal)
}
def loadGlobalSettings(base: File,
globalBase: File,
files: Seq[File],
config: LoadBuildConfiguration): LoadBuildConfiguration = {
def loadGlobalSettings(
base: File,
globalBase: File,
files: Seq[File],
config: LoadBuildConfiguration
): LoadBuildConfiguration = {
val compiled: ClassLoader => Seq[Setting[_]] =
if (files.isEmpty || base == globalBase) const(Nil)
else buildGlobalSettings(globalBase, files, config)
config.copy(injectSettings = config.injectSettings.copy(projectLoaded = compiled))
}
def buildGlobalSettings(base: File,
files: Seq[File],
config: LoadBuildConfiguration): ClassLoader => Seq[Setting[_]] = {
def buildGlobalSettings(
base: File,
files: Seq[File],
config: LoadBuildConfiguration
): ClassLoader => Seq[Setting[_]] = {
val eval = mkEval(data(config.globalPluginClasspath), base, defaultEvalOptions)
val imports = BuildUtil.baseImports ++
config.detectedGlobalPlugins.imports
val imports =
BuildUtil.baseImports ++ config.detectedGlobalPlugins.imports
loader =>
{
@ -172,10 +186,12 @@ private[sbt] object Load {
loaded.settings
}
}
def loadGlobal(state: State,
base: File,
global: File,
config: LoadBuildConfiguration): LoadBuildConfiguration =
def loadGlobal(
state: State,
base: File,
global: File,
config: LoadBuildConfiguration
): LoadBuildConfiguration =
if (base != global && global.exists) {
val gp = GlobalPlugin.load(global, state, config)
config.copy(globalPlugin = Some(gp))
@ -198,10 +214,12 @@ private[sbt] object Load {
)
}
def configInherit(lb: LoadedBuild,
ref: ResolvedReference,
config: ConfigKey,
rootProject: URI => String): Seq[ConfigKey] =
def configInherit(
lb: LoadedBuild,
ref: ResolvedReference,
config: ConfigKey,
rootProject: URI => String
): Seq[ConfigKey] =
ref match {
case pr: ProjectRef => configInheritRef(lb, pr, config)
case BuildRef(uri) => configInheritRef(lb, ProjectRef(uri, rootProject(uri)), config)
@ -226,9 +244,11 @@ private[sbt] object Load {
// 6) Load all configurations using build definitions and plugins (their classpaths and loaded instances).
// 7) Combine settings from projects, plugins, and configurations
// 8) Evaluate settings
def apply(rootBase: File,
s: State,
config: LoadBuildConfiguration): (() => Eval, BuildStructure) = {
def apply(
rootBase: File,
s: State,
config: LoadBuildConfiguration
): (() => Eval, BuildStructure) = {
val log = config.log
// load, which includes some resolution, but can't fill in project IDs yet, so follow with full resolution
@ -254,15 +274,17 @@ private[sbt] object Load {
structureIndex(data, settings, loaded.extra(data), projects)
}
val streams = timed("Load.apply: mkStreams", log) { mkStreams(projects, loaded.root, data) }
(rootEval,
new BuildStructure(projects,
loaded.root,
settings,
data,
index,
streams,
delegates,
config.scopeLocal))
val bs = new BuildStructure(
projects,
loaded.root,
settings,
data,
index,
streams,
delegates,
config.scopeLocal
)
(rootEval, bs)
}
// map dependencies on the special tasks:
@ -298,21 +320,25 @@ private[sbt] object Load {
def setDefinitionKey[T](tk: Task[T], key: ScopedKey[_]): Task[T] =
if (isDummy(tk)) tk else Task(tk.info.set(Keys.taskDefinitionKey, key), tk.work)
def structureIndex(data: Settings[Scope],
settings: Seq[Setting[_]],
extra: KeyIndex => BuildUtil[_],
projects: Map[URI, LoadedBuildUnit]): StructureIndex = {
def structureIndex(
data: Settings[Scope],
settings: Seq[Setting[_]],
extra: KeyIndex => BuildUtil[_],
projects: Map[URI, LoadedBuildUnit]
): StructureIndex = {
val keys = Index.allKeys(settings)
val attributeKeys = Index.attributeKeys(data) ++ keys.map(_.key)
val scopedKeys = keys ++ data.allKeys((s, k) => ScopedKey(s, k)).toVector
val projectsMap = projects.mapValues(_.defined.keySet)
val keyIndex = KeyIndex(scopedKeys.toVector, projectsMap)
val aggIndex = KeyIndex.aggregate(scopedKeys.toVector, extra(keyIndex), projectsMap)
new StructureIndex(Index.stringToKeyMap(attributeKeys),
Index.taskToKeyMap(data),
Index.triggers(data),
keyIndex,
aggIndex)
new StructureIndex(
Index.stringToKeyMap(attributeKeys),
Index.taskToKeyMap(data),
Index.triggers(data),
keyIndex,
aggIndex
)
}
// Reevaluates settings after modifying them. Does not recompile or reload any build components.
@ -343,9 +369,11 @@ private[sbt] object Load {
case _ => false
}
def buildConfigurations(loaded: LoadedBuild,
rootProject: URI => String,
injectSettings: InjectSettings): Seq[Setting[_]] = {
def buildConfigurations(
loaded: LoadedBuild,
rootProject: URI => String,
injectSettings: InjectSettings
): Seq[Setting[_]] = {
((loadedBuild in GlobalScope :== loaded) +:
transformProjectOnly(loaded.root, rootProject, injectSettings.global)) ++
inScope(GlobalScope)(loaded.autos.globalSettings) ++
@ -357,32 +385,33 @@ private[sbt] object Load {
val ref = ProjectRef(uri, id)
val defineConfig: Seq[Setting[_]] = for (c <- project.configurations)
yield ((configuration in (ref, ConfigKey(c.name))) :== c)
val builtin
: Seq[Setting[_]] = (thisProject :== project) +: (thisProjectRef :== ref) +: defineConfig
val builtin: Seq[Setting[_]] =
(thisProject :== project) +: (thisProjectRef :== ref) +: defineConfig
val settings = builtin ++ project.settings ++ injectSettings.project
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
transformSettings(projectScope(ref), uri, rootProject, settings)
}
val buildScope = Scope(Select(BuildRef(uri)), Zero, Zero, Zero)
val buildBase = baseDirectory :== build.localBase
val buildSettings =
transformSettings(buildScope,
uri,
rootProject,
pluginBuildSettings ++ (buildBase +: build.buildSettings))
val settings3 = pluginBuildSettings ++ (buildBase +: build.buildSettings)
val buildSettings = transformSettings(buildScope, uri, rootProject, settings3)
buildSettings ++ projectSettings
}
}
def transformProjectOnly(uri: URI,
rootProject: URI => String,
settings: Seq[Setting[_]]): Seq[Setting[_]] =
def transformProjectOnly(
uri: URI,
rootProject: URI => String,
settings: Seq[Setting[_]]
): Seq[Setting[_]] =
Project.transform(Scope.resolveProject(uri, rootProject), settings)
def transformSettings(thisScope: Scope,
uri: URI,
rootProject: URI => String,
settings: Seq[Setting[_]]): Seq[Setting[_]] =
def transformSettings(
thisScope: Scope,
uri: URI,
rootProject: URI => String,
settings: Seq[Setting[_]]
): Seq[Setting[_]] =
Project.transform(Scope.resolveScope(thisScope, uri, rootProject), settings)
def projectScope(project: Reference): Scope = Scope(Select(project), Zero, Zero, Zero)
@ -491,11 +520,13 @@ private[sbt] object Load {
val explicitRoots = unit.definitions.builds.flatMap(_.rootProject)
val projectsInRoot = if (explicitRoots.isEmpty) defined.filter(isRoot) else explicitRoots
val rootProjects = if (projectsInRoot.isEmpty) firstDefined :: Nil else projectsInRoot
(new PartBuildUnit(unit,
defined.map(d => (d.id, d)).toMap,
rootProjects.map(_.id),
buildSettings(unit)),
externals)
val partBuildUnit = new PartBuildUnit(
unit,
defined.map(d => (d.id, d)).toMap,
rootProjects.map(_.id),
buildSettings(unit)
)
(partBuildUnit, externals)
}
def buildSettings(unit: BuildUnit): Seq[Setting[_]] = {
@ -537,7 +568,7 @@ private[sbt] object Load {
def checkProjectBase(buildBase: File, projectBase: File): Unit = {
checkDirectory(projectBase)
assert(buildBase == projectBase || IO.relativize(buildBase, projectBase).isDefined,
"Directory " + projectBase + " is not contained in build root " + buildBase)
s"Directory $projectBase is not contained in build root $buildBase")
}
def checkBuildBase(base: File) = checkDirectory(base)
@ -566,9 +597,8 @@ private[sbt] object Load {
val loadedUnit = builds(refURI)
if (!(loadedUnit.defined contains refID)) {
val projectIDs = loadedUnit.defined.keys.toSeq.sorted
sys.error(
"No project '" + refID + "' in '" + refURI + "'.\nValid project IDs: " + projectIDs
.mkString(", "))
sys.error(s"""No project '$refID' in '$refURI'.
|Valid project IDs: ${projectIDs.mkString(", ")}""".stripMargin)
}
}
}
@ -596,10 +626,12 @@ private[sbt] object Load {
def resolveProjects(uri: URI, unit: PartBuildUnit, rootProject: URI => String): LoadedBuildUnit = {
IO.assertAbsolute(uri)
val resolve = (_: Project).resolve(ref => Scope.resolveProjectRef(uri, rootProject, ref))
new LoadedBuildUnit(unit.unit,
unit.defined mapValues resolve,
unit.rootProjects,
unit.buildSettings)
new LoadedBuildUnit(
unit.unit,
unit.defined mapValues resolve,
unit.rootProjects,
unit.buildSettings
)
}
def projects(unit: BuildUnit): Seq[Project] = {
@ -613,16 +645,20 @@ private[sbt] object Load {
def getRootProject(map: Map[URI, BuildUnitBase]): URI => String =
uri => getBuild(map, uri).rootProjects.headOption getOrElse emptyBuild(uri)
def getConfiguration(map: Map[URI, LoadedBuildUnit],
uri: URI,
id: String,
conf: ConfigKey): Configuration =
def getConfiguration(
map: Map[URI, LoadedBuildUnit],
uri: URI,
id: String,
conf: ConfigKey
): Configuration =
configurationOpt(map, uri, id, conf) getOrElse noConfiguration(uri, id, conf.name)
def configurationOpt(map: Map[URI, LoadedBuildUnit],
uri: URI,
id: String,
conf: ConfigKey): Option[Configuration] =
def configurationOpt(
map: Map[URI, LoadedBuildUnit],
uri: URI,
id: String,
conf: ConfigKey
): Option[Configuration] =
getProject(map, uri, id).configurations.find(_.name == conf.name)
def getProject(map: Map[URI, LoadedBuildUnit], uri: URI, id: String): ResolvedProject =
@ -664,21 +700,21 @@ private[sbt] object Load {
val hasRootAlreadyDefined = defsScala.exists(_.rootProject.isDefined)
val memoSettings = new mutable.HashMap[File, LoadedSbtFile]
def loadProjects(ps: Seq[Project], createRoot: Boolean) = {
val result = loadTransitive(ps,
normBase,
plugs,
() => eval,
config.injectSettings,
Nil,
memoSettings,
config.log,
createRoot,
uri,
config.pluginManagement.context,
Nil)
result
}
def loadProjects(ps: Seq[Project], createRoot: Boolean) =
loadTransitive(
ps,
normBase,
plugs,
() => eval,
config.injectSettings,
Nil,
memoSettings,
config.log,
createRoot,
uri,
config.pluginManagement.context,
Nil
)
val loadedProjectsRaw = timed("Load.loadUnit: loadedProjectsRaw", log) {
loadProjects(initialProjects, !hasRootAlreadyDefined)
}
@ -716,19 +752,23 @@ private[sbt] object Load {
(prev, sbtFile) =>
prev.zip(sbtFile.definitions)
}
val loadedDefs = new LoadedDefinitions(defDir,
Nil,
plugs.loader,
defs,
loadedProjects,
plugs.detected.builds.names,
valDefinitions)
val loadedDefs = new LoadedDefinitions(
defDir,
Nil,
plugs.loader,
defs,
loadedProjects,
plugs.detected.builds.names,
valDefinitions
)
new BuildUnit(uri, normBase, loadedDefs, plugs)
}
private[this] def autoID(localBase: File,
context: PluginManagement.Context,
existingIDs: Seq[String]): String = {
private[this] def autoID(
localBase: File,
context: PluginManagement.Context,
existingIDs: Seq[String]
): String = {
def normalizeID(f: File) = Project.normalizeProjectID(f.getName) match {
case Right(id) => id
case Left(msg) => sys.error(autoIDError(f, msg))
@ -753,8 +793,10 @@ private[sbt] object Load {
b.projectDefinitions(base).map(resolveBase(base))
// Lame hackery to keep track of our state.
private[this] case class LoadedProjects(projects: Seq[Project],
generatedConfigClassFiles: Seq[File])
private[this] case class LoadedProjects(
projects: Seq[Project],
generatedConfigClassFiles: Seq[File]
)
/**
* Loads a new set of projects, including any transitively defined projects underneath this one.
@ -847,18 +889,20 @@ private[sbt] object Load {
case Seq(next, rest @ _*) =>
log.debug(s"[Loading] Loading project ${next.id} @ ${next.base}")
val (finished, discovered, generated) = discoverAndLoad(next)
loadTransitive(rest ++ discovered,
buildBase,
plugins,
eval,
injectSettings,
acc :+ finished,
memoSettings,
log,
false,
buildUri,
context,
generated ++ generatedConfigClassFiles)
loadTransitive(
rest ++ discovered,
buildBase,
plugins,
eval,
injectSettings,
acc :+ finished,
memoSettings,
log,
false,
buildUri,
context,
generated ++ generatedConfigClassFiles
)
case Nil if makeOrDiscoverRoot =>
log.debug(s"[Loading] Scanning directory ${buildBase}")
discover(AddSettings.defaultSbtFiles, buildBase) match {
@ -887,18 +931,20 @@ private[sbt] object Load {
case DiscoveredProjects(None, discovered, files, generated) =>
log.debug(s"[Loading] Found non-root projects ${discovered.map(_.id).mkString(",")}")
// Here we do something interesting... We need to create an aggregate root project
val otherProjects = loadTransitive(discovered,
buildBase,
plugins,
eval,
injectSettings,
acc,
memoSettings,
log,
false,
buildUri,
context,
Nil)
val otherProjects = loadTransitive(
discovered,
buildBase,
plugins,
eval,
injectSettings,
acc,
memoSettings,
log,
false,
buildUri,
context,
Nil
)
val otherGenerated = otherProjects.generatedConfigClassFiles
val existingIds = otherProjects.projects map (_.id)
val refs = existingIds map (id => ProjectRef(buildUri, id))
@ -1001,7 +1047,7 @@ private[sbt] object Load {
expandSettings(AddSettings.allDefaults)
}
// Finally, a project we can use in buildStructure.
p.copy(settingsEval = Ev.later(allSettings))
p.copy(settings = allSettings)
.setAutoPlugins(projectPlugins)
.prefixConfigs(autoConfigs: _*)
}
@ -1032,11 +1078,13 @@ private[sbt] object Load {
// TODO - We should import vals defined in other sbt files here, if we wish to
// share. For now, build.sbt files have their own unique namespace.
def loadSettingsFile(src: File): LoadedSbtFile =
EvaluateConfigurations.evaluateSbtFile(eval(),
src,
IO.readLines(src),
loadedPlugins.detected.imports,
0)(loader)
EvaluateConfigurations.evaluateSbtFile(
eval(),
src,
IO.readLines(src),
loadedPlugins.detected.imports,
0
)(loader)
// How to merge SbtFiles we read into one thing
def merge(ls: Seq[LoadedSbtFile]): LoadedSbtFile = (LoadedSbtFile.empty /: ls) { _ merge _ }
// Loads a given file, or pulls from the cache.
@ -1085,17 +1133,21 @@ private[sbt] object Load {
val prod = (exportedProducts in Configurations.Runtime).value
val cp = (fullClasspath in Configurations.Runtime).value
val opts = (scalacOptions in Configurations.Compile).value
PluginData(removeEntries(cp, prod),
prod,
Some(fullResolvers.value),
Some(update.value),
opts)
PluginData(
removeEntries(cp, prod),
prod,
Some(fullResolvers.value),
Some(update.value),
opts
)
},
onLoadMessage := ("Loading project definition from " + baseDirectory.value)
))
private[this] def removeEntries(cp: Seq[Attributed[File]],
remove: Seq[Attributed[File]]): Seq[Attributed[File]] = {
private[this] def removeEntries(
cp: Seq[Attributed[File]],
remove: Seq[Attributed[File]]
): Seq[Attributed[File]] = {
val files = data(remove).toSet
cp filter { f =>
!files.contains(f.data)
@ -1122,15 +1174,17 @@ private[sbt] object Load {
else
noPlugins(dir, config)
def hasDefinition(dir: File) = {
def hasDefinition(dir: File): Boolean = {
import sbt.io.syntax._
(dir * -GlobFilter(DefaultTargetName)).get.nonEmpty
}
def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins =
loadPluginDefinition(dir,
config,
PluginData(config.globalPluginClasspath, Nil, None, None, Nil))
loadPluginDefinition(
dir,
config,
PluginData(config.globalPluginClasspath, Nil, None, None, Nil)
)
def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
loadPluginDefinition(dir, config, buildPluginDefinition(dir, s, config))
@ -1142,9 +1196,11 @@ private[sbt] object Load {
* @param pluginData The data required to load plugins.
* @return An instance of the loaded build with plugin information.
*/
def loadPluginDefinition(dir: File,
config: LoadBuildConfiguration,
pluginData: PluginData): LoadedPlugins = {
def loadPluginDefinition(
dir: File,
config: LoadBuildConfiguration,
pluginData: PluginData
): LoadedPlugins = {
val definitionClasspath = pluginData.definitionClasspath
val dependencyClasspath = pluginData.dependencyClasspath
val pluginLoader: ClassLoader =
@ -1162,8 +1218,10 @@ private[sbt] object Load {
* @param depcp The user-defined dependency classpath.
* @return A classpath aggregating both without repeated entries.
*/
def buildPluginClasspath(config: LoadBuildConfiguration,
depcp: Seq[Attributed[File]]): Def.Classpath = {
def buildPluginClasspath(
config: LoadBuildConfiguration,
depcp: Seq[Attributed[File]]
): Def.Classpath = {
if (depcp.isEmpty) config.classpath
else (depcp ++ config.classpath).distinct
}
@ -1177,9 +1235,11 @@ private[sbt] object Load {
* @param definitionClasspath The definition classpath for build definitions.
* @return A classloader ready to class load plugins.
*/
def pluginDefinitionLoader(config: LoadBuildConfiguration,
dependencyClasspath: Def.Classpath,
definitionClasspath: Def.Classpath): ClassLoader = {
def pluginDefinitionLoader(
config: LoadBuildConfiguration,
dependencyClasspath: Def.Classpath,
definitionClasspath: Def.Classpath
): ClassLoader = {
val manager = config.pluginManagement
val parentLoader: ClassLoader = {
if (dependencyClasspath.isEmpty) manager.initialLoader
@ -1210,21 +1270,25 @@ private[sbt] object Load {
val currentProject = session map (_.currentProject) getOrElse Map.empty
val currentBuild = session map (_.currentBuild) filter (uri =>
structure.units.keys exists (uri ==)) getOrElse structure.root
new SessionSettings(currentBuild,
projectMap(structure, currentProject),
structure.settings,
Map.empty,
Nil,
rootEval)
new SessionSettings(
currentBuild,
projectMap(structure, currentProject),
structure.settings,
Map.empty,
Nil,
rootEval
)
}
def initialSession(structure: BuildStructure, rootEval: () => Eval): SessionSettings =
new SessionSettings(structure.root,
projectMap(structure, Map.empty),
structure.settings,
Map.empty,
Nil,
rootEval)
new SessionSettings(
structure.root,
projectMap(structure, Map.empty),
structure.settings,
Map.empty,
Nil,
rootEval
)
def projectMap(structure: BuildStructure, current: Map[URI, String]): Map[URI, String] = {
val units = structure.units
@ -1244,9 +1308,11 @@ private[sbt] object Load {
final class EvaluatedConfigurations(val eval: Eval, val settings: Seq[Setting[_]])
final case class InjectSettings(global: Seq[Setting[_]],
project: Seq[Setting[_]],
projectLoaded: ClassLoader => Seq[Setting[_]]) {
final case class InjectSettings(
global: Seq[Setting[_]],
project: Seq[Setting[_]],
projectLoaded: ClassLoader => Seq[Setting[_]]
) {
import java.net.URLClassLoader
private val cache: mutable.Map[String, Seq[Setting[_]]] = mutable.Map.empty
// Cache based on the underlying URL values of the classloader
@ -1303,11 +1369,13 @@ final case class LoadBuildConfiguration(
val pluginData = globalPlugin match {
case Some(info) =>
val data = info.data
PluginData(data.fullClasspath,
data.internalClasspath,
Some(data.resolvers),
Some(data.updateReport),
Nil)
PluginData(
data.fullClasspath,
data.internalClasspath,
Some(data.resolvers),
Some(data.updateReport),
Nil
)
case None => PluginData(globalPluginClasspath)
}
val baseDir = globalPlugin match {

View File

@ -1,5 +1,5 @@
lazy val root = (project in file(".")).
aggregateSeq((if(file("aggregate").exists) Seq(sub: sbt.ProjectReference) else Nil))
aggregate((if(file("aggregate").exists) Seq(sub: sbt.ProjectReference) else Nil): _*)
lazy val sub = (project in file("sub")).
aggregate(sub2)

View File

@ -5,14 +5,14 @@ lazy val root = (project in file(".")).
)
lazy val sub: Project = project.
dependsOn(root).
settingsLazy(
name := (name in root).value + "sub"
dependsOn(LocalProject("root")).
settings(
name := (name in LocalProject("root")).value + "sub"
)
lazy val foo: Project = project.
aggregateSeq(List(root)).
dependsOnSeq(List(root)).
aggregate(LocalProject("root")).
dependsOn(LocalProject("root")).
settings(List(
name := (name in root).value + "foo"
name := (name in LocalProject("root")).value + "foo"
): _*)