redid global plugins for proper classpath handling and preparation for global settings (pending)

This commit is contained in:
Mark Harrah 2011-07-06 07:30:47 -04:00
parent 60dcd4404f
commit 353be43978
2 changed files with 76 additions and 10 deletions

49
main/GlobalPlugin.scala Normal file
View File

@ -0,0 +1,49 @@
package sbt
import Load._
import Project._
import Scoped._
import Keys._
import Configurations.Compile
import java.io.File
import org.apache.ivy.core.module.{descriptor, id}
import descriptor.ModuleDescriptor, id.ModuleRevisionId
object GlobalPlugin
{
// constructs a sequence of settings that may be appended to a project's settings to
// statically add the global plugin as a classpath dependency.
// static here meaning that the relevant tasks for the global plugin have already been evaluated
def inject(gp: GlobalPluginData): Seq[Setting[_]] =
Seq[Setting[_]](
projectDescriptors ~= { _ ++ gp.descriptors },
projectDependencies ++= gp.projectID +: gp.dependencies,
internalDependencyClasspath in Compile ~= { prev => (prev ++ gp.internalClasspath).distinct }
)
def build(base: File, s: State, config: LoadBuildConfiguration): BuildStructure = Load(base, s, config)._2
def load(base: File, s: State, config: LoadBuildConfiguration): GlobalPlugin =
{
val structure = build(base, s, config)
val data = extract(s, structure)
GlobalPlugin(data, structure, inject(data))
}
def extract(state: State, structure: BuildStructure): GlobalPluginData =
{
import structure.{data, root}
val p = RootProject(root)
val task = (projectID in p, projectDependencies in p, projectDescriptors in p, fullClasspath in (p, Compile), internalDependencyClasspath in (p, Compile) ) map GlobalPluginData.apply get data
evaluate(state, structure, task)
}
def evaluate[T](state: State, structure: BuildStructure, t: Task[T]): T =
{
import EvaluateTask._
val log = CommandSupport.logger(state)
withStreams(structure) { str =>
val nv = nodeView(state, str)
processResult(runTask(t, str, structure.index.triggers)(nv), log)
}
}
}
final case class GlobalPluginData(projectID: ModuleID, dependencies: Seq[ModuleID], descriptors: Map[ModuleRevisionId, ModuleDescriptor], fullClasspath: Seq[Attributed[File]], internalClasspath: Seq[Attributed[File]])
final case class GlobalPlugin(data: GlobalPluginData, structure: BuildStructure, inject: Seq[Setting[_]])

View File

@ -36,16 +36,20 @@ object Load
val compilers = Compiler.compilers(ClasspathOptions.boot)(state.configuration, log)
val evalPluginDef = EvaluateTask.evalPluginDef(log) _
val delegates = defaultDelegates
val inject: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings
val injectGlobal: Seq[Project.Setting[_]] = ((appConfiguration in GlobalScope) :== state.configuration) +: EvaluateTask.injectSettings
val inject = InjectSettings(injectGlobal, Nil)
val definesClass = FileValueCache(Locate.definesClass _)
val rawConfig = new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, definesClass.get, delegates, EvaluateTask.injectStreams, inject, Nil, log)
val rawConfig = new LoadBuildConfiguration(stagingDirectory, classpath, loader, compilers, evalPluginDef, definesClass.get, delegates, EvaluateTask.injectStreams, inject, None, log)
val config = loadGlobal(state, baseDirectory, defaultGlobalPlugins, rawConfig)
val result = apply(base, state, config)
definesClass.clear()
result
}
def loadGlobal(state: State, base: File, global: File, config: LoadBuildConfiguration): LoadBuildConfiguration = config
def loadGlobal(state: State, base: File, global: File, config: LoadBuildConfiguration): LoadBuildConfiguration =
if(base != global && global.exists)
config.copy(globalPlugin = Some(GlobalPlugin.load(global, state, config)))
else
config
def defaultDelegates: LoadedBuild => Scope => Seq[Scope] = (lb: LoadedBuild) => {
val rootProject = getRootProject(lb.units)
def resolveRef(project: Reference): ResolvedReference = Scope.resolveReference(lb.root, rootProject, project)
@ -87,7 +91,7 @@ object Load
val loaded = resolveProjects(load(rootBase, s, config))
val projects = loaded.units
lazy val rootEval = lazyEval(loaded.units(loaded.root).unit)
val settings = finalTransforms(config.injectSettings ++ buildConfigurations(loaded, getRootProject(projects), rootEval))
val settings = finalTransforms(config.injectSettings.global ++ buildConfigurations(loaded, getRootProject(projects), rootEval, config.injectSettings.project))
val delegates = config.delegates(loaded)
val data = Project.makeSettings(settings, delegates, config.scopeLocal)
val index = structureIndex(data)
@ -133,7 +137,7 @@ object Load
}
def isProjectThis(s: Setting[_]) = s.key.scope.project match { case This | Select(ThisProject) => true; case _ => false }
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval): Seq[Setting[_]] =
def buildConfigurations(loaded: LoadedBuild, rootProject: URI => String, rootEval: () => Eval, injectProjectSettings: Seq[Setting[_]]): Seq[Setting[_]] =
loaded.units.toSeq flatMap { case (uri, build) =>
val eval = if(uri == loaded.root) rootEval else lazyEval(build.unit)
val pluginSettings = build.unit.plugins.plugins
@ -145,7 +149,7 @@ object Load
val settings =
(thisProject :== project) +:
(thisProjectRef :== ref) +:
(defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports))
(defineConfig ++ project.settings ++ pluginThisProject ++ configurations(srcs, eval, build.imports) ++ injectProjectSettings )
// map This to thisScope, Select(p) to mapRef(uri, rootProject, p)
transformSettings(projectScope(ref), uri, rootProject, settings)
@ -333,13 +337,25 @@ object Load
new BuildUnit(uri, normBase, loadedDefs, plugs)
}
def globalPluginClasspath(config: LoadBuildConfiguration): Seq[Attributed[File]] =
config.globalPlugin match
{
case Some(cp) => cp.data.fullClasspath
case None => Nil
}
def activateGlobalPlugin(config: LoadBuildConfiguration): LoadBuildConfiguration =
config.globalPlugin match
{
case Some(gp) => config.copy(injectSettings = config.injectSettings.copy(project = gp.inject) )
case None => config
}
def plugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
if(dir.exists)
buildPlugins(dir, s, config)
buildPlugins(dir, s, activateGlobalPlugin(config))
else
noPlugins(dir, config)
def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, Nil)
def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, globalPluginClasspath(config))
def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins =
loadPluginDefinition(dir, config, buildPluginDefinition(dir, s, config))
@ -482,7 +498,8 @@ object Load
def allProjectRefs(build: URI): Seq[ProjectRef] = refs(build, allProjects(build))
private[this] def refs(build: URI, projects: Seq[ResolvedProject]): Seq[ProjectRef] = projects.map { p => ProjectRef(build, p.id) }
}
final case class LoadBuildConfiguration(stagingDirectory: File, classpath: Seq[Attributed[File]], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], definesClass: DefinesClass, delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: Seq[Setting[_]], injectDependencies: Seq[ClasspathDependency], log: Logger)
final case class LoadBuildConfiguration(stagingDirectory: File, classpath: Seq[Attributed[File]], loader: ClassLoader, compilers: Compilers, evalPluginDef: (BuildStructure, State) => Seq[Attributed[File]], definesClass: DefinesClass, delegates: LoadedBuild => Scope => Seq[Scope], scopeLocal: ScopeLocal, injectSettings: InjectSettings, globalPlugin: Option[GlobalPlugin], log: Logger)
final case class InjectSettings(global: Seq[Setting[_]], project: Seq[Setting[_]])
// information that is not original, but can be reconstructed from the rest of BuildStructure
final class StructureIndex(val keyMap: Map[String, AttributeKey[_]], val taskToKey: Map[Task[_], ScopedKey[Task[_]]], val triggers: Triggers[Task], val keyIndex: KeyIndex)