sbt-eclipse
sbt-eclipse copied to clipboard
`withSources` only works for library deps
When I eclipse generate my projects none of the plugins in project/plugins.sbt have corresponding <classpathentry sourcepath="..."> entries in the project's generated .classpath file. For library dependencies, no problem, sourcepath entries are generated correctly.
Running coursier the target plugins and their sources are downloaded so the sources are available on the file system.
// example /project/plugins.sbt
"com.typesafe.play" % "sbt-plugin" % "2.6.0-M4" withSources
sbt 0.13.15 scala 2.12.2 sbteclipse 5.1.0
From an sbt session in a test project:
> reload plugins
> eclipse
Downloads plugin sources and correctly generates .classpath file, but in project/, not the project root.
Beyond manually attaching plugin sources in Eclipse is there a way to do this via sbt-eclipse?
Why do you need the plugin or it's sources on the project classpath?
@benmccann so I can browse the plugin sources in Eclipse?
In sbt when I define libraryDependencies += "my-dep" % ... % withSources() in my build the eclipse command generates 2 files for the IDE, .classpath and .project. In the .classpath file are <classpathentry sourcepath="..."> entries for the library. I can then click a library class or method within Eclipse and be taken directly to the source.
This does not happen, for some reason, with dependencies defined in project/plugins.sbt. There are several plugins I'd like to have sources be available for in Eclipse.
Maybe I'm not explaining this properly. Basically, somehow, I'd like the eclipse command to do the equivalent of "Attach Source" manually in the IDE for project/plugins.sbt deps.
Worked around with EclipseTransformerFactory by appending sourcepath to <classpath ...> in a custom rewrite handler.
Does the trick but would be nice if sbteclipse did this out of the box when plugins defined in project/plugins.sbt have withSources appended to dep line.
@godenji Can you share the workaround? Thanks!
@mkurz sure, here's a stripped down version (have more rewrites to deal with in my implementation). Hacky, but better than manually attaching sources in Eclipse, no thanks ;-)
Add this to your settings once you have the below in place:
EclipseKeys.classpathTransformerFactories := Seq(addSourcesManaged)
import com.typesafe.sbteclipse.core.EclipsePlugin.EclipseTransformerFactory
import com.typesafe.sbteclipse.core._
import scala.xml.transform.RewriteRule
import scala.xml.{Attribute, Text, Null, Node, Elem}
import scalaz.Scalaz._
def findPath(node: Node, search: String) =
node.attributes.asAttrMap.get("path").
filter(_.contains(search))
sealed trait ClasspathAttribs
object ClasspathAttribs {
case object SOURCEPATH extends ClasspathAttribs
case object OUTPUT extends ClasspathAttribs
}
import ClasspathAttribs._
def collectRewrites(node: Node): Set[(ClasspathAttribs, String)] =
Set(
findPath(node, "/com/typesafe/play").map((SOURCEPATH, _)),
findPath(node, "/org/scala-js").map((SOURCEPATH, _))
).flatten
lazy val addSourcesManaged =
new EclipseTransformerFactory[RewriteRule] {
override def createTransformer(ref: ProjectRef, state: State): Validation[RewriteRule] = {
settingValidation(crossTarget in ref, state).map{ ct =>
new RewriteRule {
override def transform(node: Node): Seq[Node] = {
val attribs = collectRewrites(node)
node match {
case el if el.label == "classpathentry" && attribs.exists(_._1 == SOURCEPATH) =>
val elem = Elem(
el.prefix, "classpathentry", el.attributes, el.scope, true
)
attribs.headOption.map(_._2).map { str =>
elem % Attribute(
None, "sourcepath", Text(str.replace(".jar", "-sources.jar")), Null
)
} getOrElse(el)
}
}
}
}
}
}
@mkurz you'll need this as well
def structure(state: State): BuildStructure = Project.extract(state).structure
def settingValidation[A](key: SettingKey[A], state: State): Validation[A] =
key.get(structure(state).data) match {
case Some(a) => a.success
case None => "Undefined setting '%s'!".format(key.key).failureNel
}
@godenji Thanks, but I am getting:
build.sbt:16: error: not found: object ClasspathAttribs
import ClasspathAttribs._
^
build.sbt:18: error: not found: type ClasspathAttribs
def collectRewrites(node: Node): Set[(ClasspathAttribs, String)] =
^
build.sbt:20: error: not found: value SOURCEPATH
findPath(node, "/com/typesafe/play").map((SOURCEPATH, _)),
^
build.sbt:21: error: not found: value SOURCEPATH
findPath(node, "/org/scala-js").map((SOURCEPATH, _))
^
sbt.compiler.EvalException: Type error in expression
[error] sbt.compiler.EvalException: Type error in expression
Create a Transformers.scala file in your project/
trait Transformers {
... all the code
}
object Transformers extends Transformers
then in your build.sbt:
import Transformers._
Would you mind also pasting here a full example of what is added to the .classpath file by this code for one SBT plugin?
@benmccann
In the case of Play framework the plugin is split up into several different jars. Here's the original classpath entry for one of them, play-server:
<classpathentry kind="lib" path="/path/to/.coursier/cache/v1/https/repo1.maven.org/maven2/com/typesafe/play/play-server_2.12/2.6.0-M4/play-server_2.12-2.6.0-M4.jar"/>
and now with sourcepath appended:
<classpathentry sourcepath="/path/to/.coursier/cache/v1/https/repo1.maven.org/maven2/com/typesafe/play/play-server_2.12/2.6.0-M4/play-server_2.12-2.6.0-M4-sources.jar" kind="lib" path="/path/to/.coursier/cache/v1/https/repo1.maven.org/maven2/com/typesafe/play/play-server_2.12/2.6.0-M4/play-server_2.12-2.6.0-M4.jar"/>