From 8908d0e93b9c1ec1d0cc7d791b78af59c8f5d9fc Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sat, 16 Jun 2012 23:40:52 -0400 Subject: [PATCH] attempt at better error message for linkage errors in plugins. fixes #483 Only catches linkage errors during static initialization of the plugin module, but these are likely to be the most common. --- main/EvaluateTask.scala | 2 +- main/Load.scala | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/main/EvaluateTask.scala b/main/EvaluateTask.scala index 087fec19d..247f27f5c 100644 --- a/main/EvaluateTask.scala +++ b/main/EvaluateTask.scala @@ -12,7 +12,7 @@ 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]]) +final case class PluginData(classpath: Seq[Attributed[File]], resolvers: Option[Seq[Resolver]], report: Option[UpdateReport]) object EvaluateTask { import Load.BuildStructure diff --git a/main/Load.scala b/main/Load.scala index d547c0069..ed7a800e2 100755 --- a/main/Load.scala +++ b/main/Load.scala @@ -14,7 +14,7 @@ package sbt import Compiler.{Compilers,Inputs} import inc.{FileValueCache, Locate} import Project.{inScope, ScopedKey, ScopeLocal, Setting} - import Keys.{appConfiguration, baseDirectory, configuration, fullResolvers, fullClasspath, pluginData, streams, Streams, thisProject, thisProjectRef} + import Keys.{appConfiguration, baseDirectory, configuration, fullResolvers, fullClasspath, pluginData, streams, Streams, thisProject, thisProjectRef, update} import Keys.{isDummy, loadedBuild, parseResult, resolvedScoped, taskDefinitionKey} import tools.nsc.reporters.ConsoleReporter import Build.{analyzed, data} @@ -446,7 +446,7 @@ object Load } val autoPluginSettings: Seq[Setting[_]] = inScope(GlobalScope in LocalRootProject)(Seq( Keys.sbtPlugin :== true, - pluginData <<= (fullClasspath in Configurations.Runtime, fullResolvers) map ( (cp, rs) => PluginData(cp, Some(rs)) ), + pluginData <<= (fullClasspath in Configurations.Runtime, update, fullResolvers) map ( (cp, rep, rs) => PluginData(cp, Some(rs), Some(rep)) ), Keys.onLoadMessage <<= Keys.baseDirectory("Loading project definition from " + _) )) def enableSbtPlugin(config: LoadBuildConfiguration): LoadBuildConfiguration = @@ -472,7 +472,7 @@ object Load !(dir * -GlobFilter(DefaultTargetName)).get.isEmpty } def noPlugins(dir: File, config: LoadBuildConfiguration): LoadedPlugins = - loadPluginDefinition(dir, config, PluginData(config.globalPluginClasspath, None)) + loadPluginDefinition(dir, config, PluginData(config.globalPluginClasspath, None, None)) def buildPlugins(dir: File, s: State, config: LoadBuildConfiguration): LoadedPlugins = loadPluginDefinition(dir, config, buildPluginDefinition(dir, s, config)) @@ -523,10 +523,22 @@ object Load { val (pluginNames, plugins) = if(data.classpath.isEmpty) (Nil, Nil) else { val names = getPluginNames(data.classpath, loader) - (names, loadPlugins(loader, names) ) + val loaded = + try loadPlugins(loader, names) + catch { case e: LinkageError => incompatiblePlugins(data, e) } + (names, loaded) } new LoadedPlugins(dir, data, loader, plugins, pluginNames) } + private[this] def incompatiblePlugins(data: PluginData, t: LinkageError): Nothing = + { + val evicted = data.report.toList.flatMap(_.configurations.flatMap(_.evicted)) + val evictedModules = evicted map { id => (id.organization, id.name) } distinct ; + val evictedStrings = evictedModules map { case (o,n) => o + ":" + n } + val msgBase = "Binary incompatibility in plugins detected." + val msgExtra = if(evictedStrings.isEmpty) "" else "\nNote that conflicts were resolved for some dependencies:\n\t" + evictedStrings.mkString("\n\t") + throw new IncompatiblePluginsException(msgBase + msgExtra, t) + } def getPluginNames(classpath: Seq[Attributed[File]], loader: ClassLoader): Seq[String] = ( binaryPlugins(Build.data(classpath), loader) ++ (analyzed(classpath) flatMap findPlugins) ).distinct @@ -675,3 +687,4 @@ object Load val aggregateKeyIndex: KeyIndex ) } +final class IncompatiblePluginsException(msg: String, cause: Throwable) extends Exception(msg, cause) \ No newline at end of file