10

I'd like to have SBT create a file and write the project's runtime full classpath (scala, managed and unmanaged libs, project classes) for a particular stage (in this case, only for compile).

I'm trying to replicate something I did with Maven, using the maven-antrun-plugin:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <version>1.6</version>
      <executions>
        <execution>
          <id>generate-runner</id>
          <phase>package</phase>
          <configuration>
            <target>
              <property name="runtime_classpath" refid="maven.runtime.classpath" />
              <property name="runtime_entrypoint" value="com.biasedbit.webserver.Bootstrap" />

              <echo file="../../bin/run-server.sh" append="false">#!/bin/sh
java -classpath ${runtime_classpath} ${runtime_entrypoint} $$*
              </echo>
            </target>
          </configuration>
          <goals>
            <goal>run</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

How can I do this with SBT?

3 Answers 3

11

The fundamentals are right in David's answer. There are some small ways it can be improved. The java launcher can be used directly because the Scala library is included on the classpath. sbt can autodetect the main class if there is only one defined. sbt also has some methods that can make working with files easier, such as the utility methods in sbt.IO.

TaskKey[File]("mkrun") <<= (baseDirectory, fullClasspath in Runtime, mainClass in Runtime) map { (base, cp, main) =>
  val template = """#!/bin/sh
java -classpath "%s" %s "$@"
"""
  val mainStr = main getOrElse error("No main class specified")
  val contents = template.format(cp.files.absString, mainStr)
  val out = base / "../../bin/run-server.sh"
  IO.write(out, contents)
  out.setExecutable(true)
  out
}

This can go in your build.sbt directly. Alternatively, define the key separately and put it in project/Build.scala:

import sbt._
import Keys._

object MyBuild extends Build {
  val mkrun = TaskKey[File]("mkrun")
  lazy val proj = Project("demo", file(".")) settings(
    mkrun <<= ... same argument as above ...
  )
}
Sign up to request clarification or add additional context in comments.

Comments

3

You can create a task to create a file to launch the app. @Kipton Barros posted this in How do I run an sbt main class from the shell as normal command-line program?:

  val MkUnixlauncher = config("mkunixlauncher") extend(Compile)
  val mkunixlauncher = TaskKey[Unit]("mkunixlauncher")
  val mkunixlauncherTask = mkunixlauncher <<= (target, fullClasspath in Runtime) map { (target, cp) =>
    def writeFile(file: File, str: String) {
      val writer = new PrintWriter(file)
      writer.println(str)
      writer.close()
    }
    val cpString = cp.map(_.data).mkString(System.getProperty("path.separator"))
    val runtime_entrypoint = "com.biasedbit.webserver.Bootstrap"
    val launchString = """
CLASSPATH="%s"
scala -usejavacp -Djava.class.path="${CLASSPATH}" %s "$@"
""".format(cpString, entrypoint)
    val targetFile = (target / "run-server.sh").asFile
    writeFile(targetFile, launchString)
    targetFile.setExecutable(true)
  }

This creates a file named run-server.sh in your target directory that has the classpath set properly to run the app. Add mkunixlauncherTask to your buildsettings in Build.scala (or build.sbt) and then you can give the "mkunixlauncher" command to create the script.

Tweak to taste.

Comments

2

Just discovered the sbt start script plugin: https://github.com/typesafehub/xsbt-start-script-plugin:

This plugin allows you to generate a script target/start for a project. The script will run the project "in-place" (without having to build a package first).

The target/start script is similar to sbt run but it doesn't rely on SBT. sbt run is not recommended for production use because it keeps SBT itself in-memory. target/start is intended to run an app in production.

The plugin adds a task start-script which generates target/start. It also adds a stage task, aliased to the start-script task.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.