blob: ed32bfae9cdd103344f30df32a1f2bbca7b16e03 [file] [log] [blame]
package net.thunderklaus
import sbt._
import sbt.Keys._
import java.io.File
import com.earldouglas.xsbtwebplugin.WebPlugin._
import com.earldouglas.xsbtwebplugin.PluginKeys._
object GwtPlugin extends Plugin {
lazy val Gwt = config("gwt") extend (Compile)
val gwtModules = TaskKey[Seq[String]]("gwt-modules")
val gwtCompile = TaskKey[Unit]("gwt-compile", "Runs the GWT compiler")
val gwtForceCompile = TaskKey[Boolean]("gwt-force-compile", "Always recompile gwt modules")
val gwtDevMode = TaskKey[Unit]("gwt-devmode", "Runs the GWT devmode shell")
val gwtVersion = SettingKey[String]("gwt-version")
val gwtTemporaryPath = SettingKey[File]("gwt-temporary-path")
val gwtWebappPath = SettingKey[File]("gwt-webapp-path")
val gaeSdkPath = SettingKey[Option[String]]("gae-sdk-path")
var gwtModule: Option[String] = None
val gwtSetModule = Command.single("gwt-set-module") { (state, arg) =>
Project.evaluateTask(gwtModules, state) match {
case Some(Value(mods)) => {
gwtModule = mods.find(_.toLowerCase.contains(arg.toLowerCase))
gwtModule match {
case Some(m) => println("gwt-devmode will run: " + m)
case None => println("No match for '" + arg + "' in " + mods.mkString(", "))
}
}
case _ => None
}
state
}
lazy val gwtSettings: Seq[Setting[_]] = webSettings ++ gwtOnlySettings
lazy val gwtOnlySettings: Seq[Setting[_]] = inConfig(Gwt)(Defaults.configSettings) ++ Seq(
managedClasspath in Gwt <<= (managedClasspath in Compile, update) map {
(cp, up) => cp ++ Classpaths.managedJars(Provided, Set("src"), up)
},
unmanagedClasspath in Gwt <<= (unmanagedClasspath in Compile),
gwtTemporaryPath <<= (target) { (target) => target / "gwt" },
gwtWebappPath <<= (target) { (target) => target / "webapp" },
gwtVersion := "2.3.0",
gwtForceCompile := false,
gaeSdkPath := None,
libraryDependencies <++= gwtVersion(gwtVersion => Seq(
"com.google.gwt" % "gwt-user" % gwtVersion % "provided",
"com.google.gwt" % "gwt-dev" % gwtVersion % "provided",
"javax.validation" % "validation-api" % "1.0.0.GA" % "provided" withSources (),
"com.google.gwt" % "gwt-servlet" % gwtVersion)),
gwtModules <<= (javaSource in Compile, resourceDirectory in Compile) map {
(javaSource, resources) => findGwtModules(javaSource) ++ findGwtModules(resources)
},
gwtDevMode <<= (dependencyClasspath in Gwt, thisProject in Gwt, state in Gwt, javaSource in Compile, javaOptions in Gwt,
gwtModules, gaeSdkPath, gwtWebappPath, streams) map {
(dependencyClasspath, thisProject, pstate, javaSource, javaOpts, gwtModules, gaeSdkPath, warPath, s) => {
def gaeFile (path :String*) = gaeSdkPath.map(_ +: path mkString(File.separator))
val module = gwtModule.getOrElse(gwtModules.headOption.getOrElse(error("Found no .gwt.xml files.")))
val cp = dependencyClasspath.map(_.data.absolutePath) ++ getDepSources(thisProject.dependencies, pstate) ++
gaeFile("lib", "appengine-tools-api.jar").toList :+ javaSource.absolutePath
val javaArgs = javaOpts ++ (gaeFile("lib", "agent", "appengine-agent.jar") match {
case None => Nil
case Some(path) => List("-javaagent:" + path)
})
val gwtArgs = gaeSdkPath match {
case None => Nil
case Some(path) => List(
"-server", "com.google.appengine.tools.development.gwt.AppEngineLauncher")
}
val command = mkGwtCommand(
cp, javaArgs, "com.google.gwt.dev.DevMode", warPath, gwtArgs, module)
s.log.info("Running GWT devmode on: " + module)
s.log.debug("Running GWT devmode command: " + command)
command !
}
},
gwtCompile <<= (classDirectory in Compile, dependencyClasspath in Gwt, thisProject in Gwt, state in Gwt, javaSource in Compile, unmanagedSourceDirectories in Compile, javaOptions in Gwt,
gwtModules, gwtTemporaryPath, streams, gwtForceCompile) map {
(classDirectory, dependencyClasspath, thisProject, pstate, javaSource, unmanagedSource, javaOpts, gwtModules, warPath, s, force) => {
val srcDirs = Seq(javaSource.absolutePath) ++ unmanagedSource.map(_.absolutePath) ++ getDepSources(thisProject.dependencies, pstate)
val cp = Seq(classDirectory.absolutePath) ++
dependencyClasspath.map(_.data.absolutePath) ++
srcDirs
val needToCompile : Boolean = {
s.log.info("Checking GWT module updates: " + gwtModules.mkString(", "))
val gwtFiles : Seq[File] = (warPath ** "*.nocache.js").get
if(gwtFiles.isEmpty) {
s.log.info("No GWT output is found in " + warPath)
true
}
else {
val lastCompiled = gwtFiles.map(_.lastModified).max
val moduleDirs = for(d <- srcDirs; m <- (new File(d) ** "*.gwt.xml").get) yield { m.getParentFile }
val gwtSrcs = for(m <- moduleDirs; f <- (m ** "*").get) yield f
gwtSrcs.find(lastCompiled < _.lastModified).isDefined
}
}
if(force || needToCompile) {
val command = mkGwtCommand(
cp, javaOpts, "com.google.gwt.dev.Compiler", warPath, Nil, gwtModules.mkString(" "))
s.log.info("Compiling GWT modules: " + gwtModules.mkString(","))
s.log.debug("Running GWT compiler command: " + command)
command !
}
else
s.log.info("GWT modules are up to date")
}
},
webappResources in Compile <+= (gwtTemporaryPath) { (t: File) => t },
packageWar in Compile <<= (packageWar in Compile).dependsOn(gwtCompile),
commands ++= Seq(gwtSetModule)
)
def getDepSources(deps : Seq[ClasspathDep[ProjectRef]], state : State) : Set[String] = {
var sources = Set.empty[String]
val structure = Project.extract(state).structure
def get[A] = setting[A](structure)_
deps.foreach{
dep=>
sources += (get(dep.project, Keys.sourceDirectory, Compile).get.toString + "/java")
sources ++= getDepSources(Project.getProject(dep.project, structure).get.dependencies, state)
}
sources
}
def setting[T](structure: Load.BuildStructure)(ref: ProjectRef, key: SettingKey[T], configuration: Configuration): Option[T] = key in (ref, configuration) get structure.data
private def mkGwtCommand(cp: Seq[String], javaArgs: Seq[String], clazz: String, warPath: File,
gwtArgs: Seq[String], modules: String) = {
println("classpath: " + cp.mkString("\n"))
(List("java", "-cp", cp.mkString(File.pathSeparator)) ++ javaArgs ++
List(clazz, "-war", warPath.absolutePath) ++ gwtArgs :+ modules).mkString(" ")
}
private def findGwtModules(srcRoot: File): Seq[String] = {
import Path.relativeTo
val files = (srcRoot ** "*.gwt.xml").get
val relativeStrings = files.flatMap(_ x relativeTo(srcRoot)).map(_._2)
relativeStrings.map(_.dropRight(".gwt.xml".length).replace(File.separator, "."))
}
}