Skip to content

Commit

Permalink
closes #52: Customization of generated files
Browse files Browse the repository at this point in the history
  • Loading branch information
Heiko Seeberger committed Apr 11, 2012
1 parent 2b0d2e7 commit 004329e
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 98 deletions.
Expand Up @@ -20,7 +20,9 @@ package com.typesafe.sbteclipse.core

import EclipsePlugin.{
EclipseClasspathEntry,
EclipseTransformerFactory,
EclipseClasspathEntryTransformerFactory,
EclipseRewriteRuleTransformerFactory,
EclipseCreateSrc,
EclipseExecutionEnvironment,
EclipseKeys
Expand Down Expand Up @@ -51,7 +53,8 @@ import sbt.{
richFile
}
import sbt.complete.Parser
import scala.xml.{ Elem, PrettyPrinter }
import scala.xml.{ Node, PrettyPrinter }
import scala.xml.transform.{ RewriteRule, RuleTransformer }
import scalaz.{ Failure, NonEmptyList, Success }
import scalaz.Scalaz._
import scalaz.effects._
Expand Down Expand Up @@ -107,6 +110,8 @@ private object Eclipse {
} yield {
val configs = configurations(ref, state)
val applic = classpathEntryTransformerFactory(ref, state).createTransformer(ref, state) |@|
(classpathTransformerFactories(ref, state) map (_.createTransformer(ref, state))).sequence[Validation, RewriteRule] |@|
(projectTransformerFactories(ref, state) map (_.createTransformer(ref, state))).sequence[Validation, RewriteRule] |@|
name(ref, state) |@|
buildDirectory(state) |@|
baseDirectory(ref, state) |@|
Expand Down Expand Up @@ -151,6 +156,8 @@ private object Eclipse {
relativizeLibs: Boolean,
state: State)(
classpathEntryTransformer: Seq[EclipseClasspathEntry] => Seq[EclipseClasspathEntry],
classpathTransformers: Seq[RewriteRule],
projectTransformers: Seq[RewriteRule],
name: String,
buildDirectory: File,
baseDirectory: File,
Expand All @@ -161,7 +168,7 @@ private object Eclipse {
for {
_ <- executePreTasks(preTasks, state)
n <- io(name)
_ <- saveXml(baseDirectory / ".project", projectXml(name))
_ <- saveXml(baseDirectory / ".project", new RuleTransformer(projectTransformers: _*)(projectXml(name)))
cp <- classpath(
classpathEntryTransformer,
buildDirectory,
Expand All @@ -173,15 +180,15 @@ private object Eclipse {
jreContainer,
state
)
_ <- saveXml(baseDirectory / ".classpath", cp)
_ <- saveXml(baseDirectory / ".classpath", new RuleTransformer(classpathTransformers: _*)(cp))
_ <- saveProperties(baseDirectory / ".settings" / "org.scala-ide.sdt.core.prefs", scalacOptions)
} yield n
}

def executePreTasks(preTasks: Seq[(TaskKey[_], ProjectRef)], state: State): IO[Unit] =
io(for ((preTask, ref) <- preTasks) evaluateTask(preTask, ref, state))

def projectXml(name: String): Elem =
def projectXml(name: String): Node =
<projectDescription>
<name>{ name }</name>
<buildSpec>
Expand All @@ -204,7 +211,7 @@ private object Eclipse {
externalDependencies: Seq[Lib],
projectDependencies: Seq[String],
jreContainer: String,
state: State): IO[Elem] = {
state: State): IO[Node] = {
val srcEntriesIoSeq =
for ((dir, output) <- srcDirectories) yield srcEntry(baseDirectory, dir, output, state)
for (srcEntries <- srcEntriesIoSeq.sequence) yield {
Expand Down Expand Up @@ -398,9 +405,22 @@ private object Eclipse {
def withSource(ref: Reference, state: State): Boolean =
setting(EclipseKeys.withSource in ref, state).fold(_ => false, id)

def classpathEntryTransformerFactory(ref: Reference, state: State): EclipseClasspathEntryTransformerFactory =
setting(EclipseKeys.classpathEntryTransformerFactory in ref, state).fold(_ =>
EclipseClasspathEntryTransformerFactory.Default, id
def classpathEntryTransformerFactory(ref: Reference, state: State): EclipseTransformerFactory[Seq[EclipseClasspathEntry] => Seq[EclipseClasspathEntry]] =
setting(EclipseKeys.classpathEntryTransformerFactory in ref, state).fold(
_ => EclipseClasspathEntryTransformerFactory.Identity,
id
)

def classpathTransformerFactories(ref: Reference, state: State): Seq[EclipseTransformerFactory[RewriteRule]] =
setting(EclipseKeys.classpathTransformerFactories in ref, state).fold(
_ => Seq(EclipseRewriteRuleTransformerFactory.ClasspathDefault),
EclipseRewriteRuleTransformerFactory.ClasspathDefault +: _
)

def projectTransformerFactories(ref: Reference, state: State): Seq[EclipseTransformerFactory[RewriteRule]] =
setting(EclipseKeys.projectTransformerFactories in ref, state).fold(
_ => Seq(EclipseRewriteRuleTransformerFactory.Identity),
id
)

def configurations(ref: Reference, state: State): Seq[Configuration] =
Expand All @@ -423,7 +443,7 @@ private object Eclipse {

// IO

def saveXml(file: File, xml: Elem): IO[Unit] =
def saveXml(file: File, xml: Node): IO[Unit] =
fileWriter(file).bracket(closeWriter)(writer => io(writer.write(new PrettyPrinter(999, 2) format xml)))

def saveProperties(file: File, settings: Seq[(String, String)]): IO[Unit] =
Expand Down Expand Up @@ -457,8 +477,8 @@ private object Eclipse {
private case class Content(
name: String,
dir: File,
project: Elem,
classpath: Elem,
project: Node,
classpath: Node,
scalacOptions: Seq[(String, String)])

private case class Lib(binary: File)(val source: Option[File])
Expand Up @@ -32,7 +32,8 @@ import sbt.{
}
import sbt.Keys.{ baseDirectory, commands }
import scala.util.control.Exception
import scala.xml.{ Attribute, Elem, Null, Text }
import scala.xml.{ Attribute, Elem, MetaData, Node, Null, Text }
import scala.xml.transform.RewriteRule

object EclipsePlugin extends EclipsePlugin

Expand All @@ -49,57 +50,66 @@ trait EclipsePlugin {
object EclipseKeys {
import EclipseOpts._

val executionEnvironment: SettingKey[Option[EclipseExecutionEnvironment.Value]] =
SettingKey[Option[EclipseExecutionEnvironment.Value]](
prefix(ExecutionEnvironment),
"The optional Eclipse execution environment.")

val skipParents: SettingKey[Boolean] =
SettingKey[Boolean](
prefix(SkipParents),
"Skip creating Eclipse files for parent project?")

val withSource: SettingKey[Boolean] =
SettingKey[Boolean](
prefix(WithSource),
"Download and link sources for library dependencies?")

val classpathEntryTransformerFactory: SettingKey[EclipseClasspathEntryTransformerFactory] =
SettingKey[EclipseClasspathEntryTransformerFactory](
prefix("classpathEntryTransformerFactory"),
"Creates a transformer for classpath entries.")

val commandName: SettingKey[String] =
SettingKey[String](
prefix("command-name"),
"The name of the command.")

val configurations: SettingKey[Set[Configuration]] =
SettingKey[Set[Configuration]](
prefix("configurations"),
"The configurations to take into account.")

val createSrc: SettingKey[EclipseCreateSrc.ValueSet] =
SettingKey[EclipseCreateSrc.ValueSet](
prefix("create-src"),
"The source kinds to be included."
)

val eclipseOutput: SettingKey[Option[String]] =
SettingKey[Option[String]](
prefix("eclipse-output"),
"The optional output for Eclipse.")

val preTasks: SettingKey[Seq[TaskKey[_]]] =
SettingKey[Seq[TaskKey[_]]](
prefix("pre-tasks"),
"The tasks to be evaluated prior to creating the Eclipse project definition."
)

val relativizeLibs: SettingKey[Boolean] =
SettingKey[Boolean](
prefix("relativize-libs"),
"Relativize the paths to the libraries?")
val executionEnvironment: SettingKey[Option[EclipseExecutionEnvironment.Value]] = SettingKey(
prefix(ExecutionEnvironment),
"The optional Eclipse execution environment."
)

val skipParents: SettingKey[Boolean] = SettingKey(
prefix(SkipParents),
"Skip creating Eclipse files for parent project?"
)

val withSource: SettingKey[Boolean] = SettingKey(
prefix(WithSource),
"Download and link sources for library dependencies?"
)

@deprecated("Use classpathTransformerFactories instead!", "2.1.0")
val classpathEntryTransformerFactory: SettingKey[EclipseTransformerFactory[Seq[EclipseClasspathEntry] => Seq[EclipseClasspathEntry]]] = SettingKey(
prefix("classpathEntryTransformerFactory"),
"Creates a transformer for classpath entries."
)

val classpathTransformerFactories: SettingKey[Seq[EclipseTransformerFactory[RewriteRule]]] = SettingKey(
prefix("classpathTransformerFactory"),
"Factories for a rewrite rule for the .classpath file."
)

val projectTransformerFactories: SettingKey[Seq[EclipseTransformerFactory[RewriteRule]]] = SettingKey(
prefix("projectTransformerFactory"),
"Factories for a rewrite rule for the .project file."
)

val commandName: SettingKey[String] = SettingKey(
prefix("command-name"),
"The name of the command."
)

val configurations: SettingKey[Set[Configuration]] = SettingKey(
prefix("configurations"),
"The configurations to take into account."
)

val createSrc: SettingKey[EclipseCreateSrc.ValueSet] = SettingKey(
prefix("create-src"),
"The source kinds to be included."
)

val eclipseOutput: SettingKey[Option[String]] = SettingKey(
prefix("eclipse-output"),
"The optional output for Eclipse."
)

val preTasks: SettingKey[Seq[TaskKey[_]]] = SettingKey(
prefix("pre-tasks"),
"The tasks to be evaluated prior to creating the Eclipse project definition."
)

val relativizeLibs: SettingKey[Boolean] = SettingKey(
prefix("relativize-libs"),
"Relativize the paths to the libraries?"
)

private def prefix(key: String) = "eclipse-" + key
}
Expand All @@ -124,7 +134,7 @@ trait EclipsePlugin {
}

sealed trait EclipseClasspathEntry {
def toXml: Elem
def toXml: Node
}

object EclipseClasspathEntry {
Expand Down Expand Up @@ -169,38 +179,68 @@ trait EclipsePlugin {
val All = ValueSet(Unmanaged, Managed, Source, Resource)
}

trait EclipseClasspathEntryTransformerFactory {
def createTransformer(
ref: ProjectRef,
state: State): Validation[Seq[EclipseClasspathEntry] => Seq[EclipseClasspathEntry]]
trait EclipseTransformerFactory[A] {
def createTransformer(ref: ProjectRef, state: State): Validation[A]
}

object EclipseClasspathEntryTransformerFactory {

object Identity extends EclipseClasspathEntryTransformerFactory {
import scalaz.Scalaz._
override def createTransformer(
ref: ProjectRef,
state: State): Validation[Seq[EclipseClasspathEntry] => Seq[EclipseClasspathEntry]] =
((entries: Seq[EclipseClasspathEntry]) => entries).success
}

object Default extends EclipseClasspathEntryTransformerFactory {
object Identity extends EclipseTransformerFactory[Seq[EclipseClasspathEntry] => Seq[EclipseClasspathEntry]] {
import scalaz.Scalaz._
override def createTransformer(
ref: ProjectRef,
state: State): Validation[Seq[EclipseClasspathEntry] => Seq[EclipseClasspathEntry]] = {
val transformer =
(entries: Seq[EclipseClasspathEntry]) => entries collect {
case EclipseClasspathEntry.Lib(path, _) if path contains "scala-library.jar" =>
EclipseClasspathEntry.Con("org.scala-ide.sdt.launching.SCALA_CONTAINER")
case EclipseClasspathEntry.Lib(path, _) if path contains "scala-compiler.jar" =>
EclipseClasspathEntry.Con("org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER")
case entry =>
entry
}
val transformer = (entries: Seq[EclipseClasspathEntry]) => entries
transformer.success
}
}
}

object EclipseRewriteRuleTransformerFactory {

object IdentityRewriteRule extends RewriteRule {
override def transform(node: Node): Node = node
}

object ClasspathDefaultRule extends RewriteRule {

private val CpEntry = "classpathentry"

private val ScalaContainer = "org.scala-ide.sdt.launching.SCALA_CONTAINER"

private val ScalaCompilerContainer = "org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"

override def transform(node: Node): Seq[Node] = node match {
case Elem(pf, CpEntry, attrs, scope, child @ _*) if isScalaLibrary(attrs) =>
Elem(pf, CpEntry, container(ScalaContainer), scope, child: _*)
case Elem(pf, CpEntry, attrs, scope, child @ _*) if isScalaCompiler(attrs) =>
Elem(pf, CpEntry, container(ScalaCompilerContainer), scope, child: _*)
case other =>
other
}

private def container(name: String) =
Attribute("kind", Text("con"), Attribute("path", Text(name), Null))

private def isScalaLibrary(metaData: MetaData) =
metaData("kind") == Text("lib") &&
(Option(metaData("path").text) map (_ contains "scala-library.jar") getOrElse false)

private def isScalaCompiler(metaData: MetaData) =
metaData("kind") == Text("lib") &&
(Option(metaData("path").text) map (_ contains "scala-compiler.jar") getOrElse false)
}

object Identity extends EclipseTransformerFactory[RewriteRule] {
import scalaz.Scalaz._
override def createTransformer(ref: ProjectRef, state: State): Validation[RewriteRule] =
IdentityRewriteRule.success
}

object ClasspathDefault extends EclipseTransformerFactory[RewriteRule] {
import scalaz.Scalaz._
override def createTransformer(ref: ProjectRef, state: State): Validation[RewriteRule] =
ClasspathDefaultRule.success
}
}
}
Expand Up @@ -140,6 +140,7 @@ TaskKey[Unit]("verify-classpath-xml-subb") <<= baseDirectory map { dir =>

TaskKey[Unit]("verify-classpath-xml-subc") <<= baseDirectory map { dir =>
val classpath = XML.loadFile(dir / "sub" / "subc" / ".classpath")
val project = XML.loadFile(dir / "sub" / "subc" / ".project")
if ((classpath \ "classpathentry") != (classpath \ "classpathentry").distinct)
error("Expected .classpath of subc project not to contain duplicate entries: %s" format classpath)
// src entries
Expand All @@ -148,6 +149,13 @@ TaskKey[Unit]("verify-classpath-xml-subc") <<= baseDirectory map { dir =>
// lib entries with absolute paths
if (!(classpath.child contains <classpathentry kind="lib" path={ "%s/lib_managed/jars/biz.aQute/bndlib/bndlib-1.50.0.jar".format(dir.getCanonicalPath) } />))
error("""Expected .classpath of subc project to contain <classpathentry kind="lib" path="%s/lib_managed/jars/biz.aQute/bndlib/bndlib-1.50.0.jar" />: %s""".format(dir.getCanonicalPath, classpath))
// classpath transformer
if (!(classpath.child contains <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>))
error("""Expected .classpath of root project to contain <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> """)
if (!(classpath.child contains <foo bar="baz"/>))
error("""Expected .classpath of subc project to contain <foo bar="baz"/>!""")
if (!(project.child contains <foo bar="baz"/>))
error("""Expected .project of subc project to contain <foo bar="baz"/>!""")
}

TaskKey[Unit]("verify-settings") <<= baseDirectory map { dir =>
Expand Down

0 comments on commit 004329e

Please sign in to comment.