1
votes

I created a reproducible example about a problem I'm experiencing with SBT plugin resolution:

https://github.com/NicolasRouquette/sbt.problem.example

This example has two SBT projects:

  • test.plugin a simple SBT plugin
  • test.app a simple SBT project that uses the test.plugin

There is also a local Maven repository to which the test.plugin is published with a POM file that includes properties like this:

<properties>
    <git.branch>master</git.branch>
    <git.commit>fe2dc11d6fbb85c5ce0e83b031bbd425997bbd59</git.commit>
    <git.tags></git.tags>
</properties>
<properties>
    <scalaVersion>2.10</scalaVersion>
    <sbtVersion>0.13</sbtVersion>
    <extraDependencyAttributes xml:space="preserve">+e:sbtVersion:#@#:+0.13:#@#:+module:#@#:+sbt-license plugin:#@#:+e:scalaVersion:#@#:+2.10:#@#:+organisation:#@#:+com.banno:#@#:+branch:#@#:+@#:NULL:#@:#@#:+revision:#@#:+0.1.5:#@#:
    +e:sbtVersion:#@#:+0.13:#@#:+module:#@#:+sbt-license-report:#@#:+e:scalaVersion:#@#:+2.10:#@#:+organisation:#@#:+com.typesafe.sbt:#@#:+branch:#@#:+@#:NULL:#@:#@#:+revision:#@#:+1.0.0:#@#:
    +e:sbtVersion:#@#:+0.13:#@#:+module:#@#:+sbt-git:#@#:+e:scalaVersion:#@#:+2.10:#@#:+organisation:#@#:+com.typesafe.sbt:#@#:+branch:#@#:+@#:NULL:#@:#@#:+revision:#@#:+0.8.5:#@#:
    +e:sbtVersion:#@#:+0.13:#@#:+module:#@#:+aether-deploy:#@#:+e:scalaVersion:#@#:+2.10:#@#:+organisation:#@#:+no.arktekk.sbt:#@#:+branch:#@#:+@#:NULL:#@:#@#:+revision:#@#:+0.16:#@#:
    </extraDependencyAttributes>
</properties>

I can't run SBT on test.app because SBT fails to resolve the test.plugin:

addSbtPlugin("org.test" % "test-plugin" % "1.0")


[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: org.test#test-plugin;1.0: not found
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn] 
[warn]  Note: Some unresolved dependencies have extra attributes.  Check that these dependencies exist with the requested attributes.
[warn]          org.test:test-plugin:1.0 (sbtVersion=0.13, scalaVersion=2.10)
[warn] 
[warn]  Note: Unresolved dependencies path:
[warn]          org.test:test-plugin:1.0 (sbtVersion=0.13, scalaVersion=2.10) (/opt/local/imce/users/nfr/github.imce/example/test.app/project/plugins.sbt#L6-7)
[warn]            +- default:test-app-build:0.1-SNAPSHOT (sbtVersion=0.13, scalaVersion=2.10)
sbt.ResolveException: unresolved dependency: org.test#test-plugin;1.0: not found
        at sbt.IvyActions$.sbt$IvyActions$$resolve(IvyActions.scala:294)
        at sbt.IvyActions$$anonfun$updateEither$1.apply(IvyActions.scala:191)
        at sbt.IvyActions$$anonfun$updateEither$1.apply(IvyActions.scala:168)
        at sbt.IvySbt$Module$$anonfun$withModule$1.apply(Ivy.scala:155)
        at sbt.IvySbt$Module$$anonfun$withModule$1.apply(Ivy.scala:155)
        at sbt.IvySbt$$anonfun$withIvy$1.apply(Ivy.scala:132)
        at sbt.IvySbt.sbt$IvySbt$$action$1(Ivy.scala:57)
...

Based on trying to understand what's going on with the debugger, I get the impression that:

  • resolving plugins happens very early when running SBT; which means that it involves a lot of SBT/Ivy machinery
  • I don't quite understand how the SBT/Ivy machinery resolves SBT plugins published to a Maven repository (though I've seen some kind of Maven/Ivy conversion somewhere)
  • it seems that the Maven POM properties are not analyzed to retrieve the sbtVersion (e.g. 0.13) and scalaBinaryVersion (e.g. 2.10) of an artifact

Can someone confirm/correct my analysis?

Is there a way to make this maven-published plugin dependency lookup work?

1

1 Answers

2
votes

Thanks for the repro project.

First issue is that you have two <properties> element. sbt will use both the URL layout and the content of the POM to resolve the plugin. Here's how to merge <properties> element. Not sure if there's more elegant way:

makePom := {
  val old = makePom.value
  val pom = xml.XML.loadFile(old)
  val additionalProperties =
    (<git.branch>{git.gitCurrentBranch.value}</git.branch>
    <git.commit>{git.gitHeadCommit.value.getOrElse("N/A")+(if (git.gitUncommittedChanges.value) "-SNAPSHOT" else "")}</git.commit>
    <git.tags>{git.gitCurrentTags.value}</git.tags>)
  val newPom = pom.copy(child = pom.child.toSeq map {
    case elem: xml.Elem if elem.label == "properties" =>
      elem.copy(child = elem.child ++ additionalProperties)
    case x => x
  })
  xml.XML.save(old.toString, newPom, enc = "UTF-8", xmlDecl = true)
  old
}

Next, you should've seen something like this when you tried to resolve the plugin:

[warn] ==== Local Test: tried
[warn]   file:/xxx/sbt.problem.example/test.app/project/../local.repo/org/test/test-plugin_2.10_0.13/1.0/test-plugin-1.0.pom

See that test.app/project/../local.repo/ would become test.app/local.repo. Instead of ".." here's what you can do in test.app/project/plugins.sbt:

resolvers += new MavenCache("Local Test", baseDirectory.value.getParentFile.getParentFile / "local.repo")

addSbtPlugin("org.test" % "test-plugin" % "1.0")