Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

withSources only works for library deps #336

Open
godenji opened this issue Apr 21, 2017 · 11 comments
Open

withSources only works for library deps #336

godenji opened this issue Apr 21, 2017 · 11 comments

Comments

@godenji
Copy link
Contributor

godenji commented Apr 21, 2017

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

@godenji
Copy link
Contributor Author

godenji commented Apr 21, 2017

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?

@benmccann
Copy link
Contributor

Why do you need the plugin or it's sources on the project classpath?

@godenji
Copy link
Contributor Author

godenji commented Apr 22, 2017

@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.

@godenji
Copy link
Contributor Author

godenji commented Apr 23, 2017

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.

@mkurz
Copy link
Member

mkurz commented Apr 23, 2017

@godenji Can you share the workaround? Thanks!

@godenji
Copy link
Contributor Author

godenji commented Apr 23, 2017

@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)
             }
           }
         }
       }
     }
   }

@godenji
Copy link
Contributor Author

godenji commented Apr 23, 2017

@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
  }

@mkurz
Copy link
Member

mkurz commented Apr 23, 2017

@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

@godenji
Copy link
Contributor Author

godenji commented Apr 23, 2017

Create a Transformers.scala file in your project/

trait Transformers {
  ... all the code
}
object Transformers extends Transformers

then in your build.sbt:
import Transformers._

@benmccann
Copy link
Contributor

Would you mind also pasting here a full example of what is added to the .classpath file by this code for one SBT plugin?

@godenji
Copy link
Contributor Author

godenji commented Apr 23, 2017

@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"/>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants