Permalink
Browse files

Proper isolation of build definition classes. Fixes #536, #511.

  • Loading branch information...
1 parent 9d39b5d commit ed902ea56564c7529fd1c28d58750926d98dd088 @harrah committed Aug 30, 2012
View
12 main/EvaluateTask.scala
@@ -12,7 +12,17 @@ package sbt
import scala.Console.RED
final case class EvaluateConfig(cancelable: Boolean, restrictions: Seq[Tags.Rule], checkCycles: Boolean = false)
-final case class PluginData(classpath: Seq[Attributed[File]], resolvers: Option[Seq[Resolver]], report: Option[UpdateReport])
+final case class PluginData(dependencyClasspath: Seq[Attributed[File]], definitionClasspath: Seq[Attributed[File]], resolvers: Option[Seq[Resolver]], report: Option[UpdateReport])
+{
+ val classpath: Seq[Attributed[File]] = definitionClasspath ++ dependencyClasspath
+}
+object PluginData
+{
+ @deprecated("Use the alternative that specifies the specific classpaths.", "0.13.0")
+ def apply(classpath: Seq[Attributed[File]], resolvers: Option[Seq[Resolver]], report: Option[UpdateReport]): PluginData =
+ PluginData(classpath, Nil, resolvers, report)
+}
+
object EvaluateTask
{
import Load.BuildStructure
View
43 main/Load.scala
@@ -15,7 +15,7 @@ package sbt
import inc.{FileValueCache, Locate}
import Project.{inScope, ScopedKey, ScopeLocal, Setting}
import Keys.{appConfiguration, baseDirectory, configuration, fullResolvers, fullClasspath, pluginData, streams, Streams, thisProject, thisProjectRef, update}
- import Keys.{isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey}
+ import Keys.{exportedProducts, isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey}
import tools.nsc.reporters.ConsoleReporter
import Build.{analyzed, data}
import Scope.{GlobalScope, ThisScope}
@@ -448,9 +448,17 @@ object Load
}
val autoPluginSettings: Seq[Setting[_]] = inScope(GlobalScope in LocalRootProject)(Seq(
Keys.sbtPlugin :== true,
- pluginData <<= (fullClasspath in Configurations.Runtime, update, fullResolvers) map ( (cp, rep, rs) => PluginData(cp, Some(rs), Some(rep)) ),
+ pluginData <<= (exportedProducts in Configurations.Runtime, fullClasspath in Configurations.Runtime, update, fullResolvers) map ( (prod, cp, rep, rs) =>
+ PluginData(removeEntries(cp, prod), prod, Some(rs), Some(rep))
+ ),
Keys.onLoadMessage <<= Keys.baseDirectory("Loading project definition from " + _)
))
+ 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) }
+ }
+
def enableSbtPlugin(config: LoadBuildConfiguration): LoadBuildConfiguration =
config.copy(injectSettings = config.injectSettings.copy(
global = autoPluginSettings ++ config.injectSettings.global,
@@ -480,15 +488,32 @@ object Load
def loadPluginDefinition(dir: File, config: LoadBuildConfiguration, pluginData: PluginData): LoadedPlugins =
{
- val (definitionClasspath, pluginLoader) = pluginDefinitionLoader(config, pluginData.classpath)
- loadPlugins(dir, pluginData.copy(classpath = definitionClasspath), pluginLoader)
+ val (definitionClasspath, pluginLoader) = pluginDefinitionLoader(config, pluginData)
+ loadPlugins(dir, pluginData.copy(dependencyClasspath = definitionClasspath), pluginLoader)
}
- def pluginDefinitionLoader(config: LoadBuildConfiguration, pluginClasspath: Seq[Attributed[File]]): (Seq[Attributed[File]], ClassLoader) =
+ def pluginDefinitionLoader(config: LoadBuildConfiguration, dependencyClasspath: Seq[Attributed[File]]): (Seq[Attributed[File]], ClassLoader) =
+ pluginDefinitionLoader(config, dependencyClasspath, Nil)
+ def pluginDefinitionLoader(config: LoadBuildConfiguration, pluginData: PluginData): (Seq[Attributed[File]], ClassLoader) =
+ pluginDefinitionLoader(config, pluginData.dependencyClasspath, pluginData.definitionClasspath)
+ def pluginDefinitionLoader(config: LoadBuildConfiguration, depcp: Seq[Attributed[File]], defcp: Seq[Attributed[File]]): (Seq[Attributed[File]], ClassLoader) =
{
- val definitionClasspath = if(pluginClasspath.isEmpty) config.classpath else (pluginClasspath ++ config.classpath).distinct
+ val definitionClasspath =
+ if(depcp.isEmpty)
+ config.classpath
+ else
+ (depcp ++ config.classpath).distinct
val pm = config.pluginManagement
- def addToLoader() = pm.loader add Path.toURLs(data(pluginClasspath))
- val pluginLoader = if(pluginClasspath.isEmpty) pm.initialLoader else { addToLoader(); pm.loader }
+ // only the dependencyClasspath goes in the common plugin class loader ...
+ def addToLoader() = pm.loader add Path.toURLs(data(depcp))
+
+ val parentLoader = if(depcp.isEmpty) pm.initialLoader else { addToLoader(); pm.loader }
+ val pluginLoader =
+ if(defcp.isEmpty)
+ parentLoader
+ else {
+ // ... the build definition classes get their own loader so that they don't conflict with other build definitions (#511)
+ ClasspathUtilities.toLoader(data(defcp), parentLoader)
+ }
(definitionClasspath, pluginLoader)
}
def buildPluginDefinition(dir: File, s: State, config: LoadBuildConfiguration): PluginData =
@@ -607,7 +632,7 @@ object Load
final class LoadedPlugins(val base: File, val pluginData: PluginData, val loader: ClassLoader, val plugins: Seq[Plugin], val pluginNames: Seq[String])
{
def fullClasspath: Seq[Attributed[File]] = pluginData.classpath
- def classpath = data(fullClasspath)
+ def classpath: Seq[File] = data(fullClasspath)
}
final class BuildUnit(val uri: URI, val localBase: File, val definitions: LoadedDefinitions, val plugins: LoadedPlugins)
{
View
5 sbt/src/sbt-test/project/isolated-build-definitions/build.sbt
@@ -0,0 +1,5 @@
+name := "project-runtime"
+
+scalaVersion := "2.9.1"
+
+crossPaths := false
View
5 sbt/src/sbt-test/project/isolated-build-definitions/generator/build.sbt
@@ -0,0 +1,5 @@
+name := "project"
+
+scalaVersion := "2.9.1"
+
+crossPaths := false
View
7 sbt/src/sbt-test/project/isolated-build-definitions/generator/project/Build.scala
@@ -0,0 +1,7 @@
+import sbt._
+import Keys._
+
+object B extends Build {
+lazy val project = Project(id = "project",
+ base = file("."))
+}
View
11 sbt/src/sbt-test/project/isolated-build-definitions/project/Build.scala
@@ -0,0 +1,11 @@
+import sbt._
+import Keys._
+
+object B extends Build
+{
+ lazy val projectRuntime = Project(id = "project-runtime", base = file(".")).
+ dependsOn(projectGenerator % "optional")
+
+
+ lazy val projectGenerator = ProjectRef(uri("generator/"),"project")
+}
View
5 sbt/src/sbt-test/project/isolated-build-definitions/test
@@ -0,0 +1,5 @@
+# This test checks loading successfully.
+# There are two builds that define a Build with the same identifier.
+# If the build definitions are incorrectly in the same class loader, one will shadow the other.
+> {generator/}project/clean
+> project-runtime/clean

0 comments on commit ed902ea

Please sign in to comment.