Skip to content

Commit

Permalink
Merge pull request #647 from gzoller/wip/ash
Browse files Browse the repository at this point in the history
wip/ash
  • Loading branch information
muuki88 committed Aug 13, 2015
2 parents 3664a7b + 1534109 commit ea4f9cf
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.typesafe.sbt
package packager
package archetypes

import sbt._
import sbt.Keys.{ mappings, target, name, mainClass, sourceDirectory, javaOptions, streams }
import packager.Keys.{ packageName, executableScriptName }
import linux.{ LinuxFileMetaData, LinuxPackageMapping }
import linux.LinuxPlugin.autoImport.{ linuxPackageMappings, defaultLinuxInstallLocation }
import SbtNativePackager.{ Universal, Debian }

/**
* == Java Application ==
*
* This class is an alternate to JavaAppPackaging designed to support the ash shell. JavaAppPackaging
* generates bash-specific code that is not compatible with ash, a very stripped-down, lightweight shell
* used by popular micro base Docker images like BusyBox. The AshScriptPlugin will generate simple
* ash-compatible output.
*
* Just like with JavaAppPackaging you can override the bash-template file by creating a src/templates
* directory and adding your own bash-template file. Actually this isn't a bad idea as the default
* bash-template file inherited from JavaAppPackaging has a lot of stuff you probably don't want/need
* in a highly-constrained environment like ash+BusyBox. Something much simpler will do, for example:
*
* #!/usr/bin/env sh
*
* realpath () {
* (
* TARGET_FILE="$1"
*
* cd "$(dirname "$TARGET_FILE")"
* TARGET_FILE=$(basename "$TARGET_FILE")
*
* COUNT=0
* while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ]
* do
* TARGET_FILE=$(readlink "$TARGET_FILE")
* cd "$(dirname "$TARGET_FILE")"
* TARGET_FILE=$(basename "$TARGET_FILE")
* COUNT=$(($COUNT + 1))
* done
*
* if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then
* cd "$TARGET_FILE"
* TARGET_FILEPATH=
* else
* TARGET_FILEPATH=/$TARGET_FILE
* fi
*
* echo "$(pwd -P)/$TARGET_FILE"
* )
* }
*
* real_script_path="$(realpath "$0")"
* app_home="$(realpath "$(dirname "$real_script_path")")"
* lib_dir="$(realpath "${app_home}/../lib")"
*
* ${{template_declares}}
*
* java -classpath $app_classpath $app_mainclass $@
*
*
* == Configuration ==
*
* This plugin adds new settings to configure your packaged application.
* The keys are defined in [[com.typesafe.sbt.packager.archetypes.JavaAppKeys]]
*
* @example Enable this plugin in your `build.sbt` with
*
* {{{
* enablePlugins(AshScriptPlugin)
* }}}
*/
object AshScriptPlugin extends AutoPlugin {

val bashTemplate = "bash-template"

override def requires = JavaAppPackaging

//object autoImport extends JavaAppKeys

import JavaAppPackaging.autoImport._

override def projectSettings = Seq(
makeBashScript <<= (bashScriptTemplateLocation, bashScriptDefines, target in Universal, executableScriptName, sourceDirectory) map makeUniversalAshScript,
bashScriptDefines <<= (Keys.mainClass in (Compile, bashScriptDefines), scriptClasspath in bashScriptDefines, bashScriptExtraDefines, bashScriptConfigLocation) map { (mainClass, cp, extras, config) =>
val hasMain =
for {
cn <- mainClass
} yield JavaAppAshScript.makeDefines(cn, appClasspath = cp, extras = extras, configFile = config)
hasMain getOrElse Nil
}
)

def makeUniversalAshScript(defaultTemplateLocation: File, defines: Seq[String], tmpDir: File, name: String, sourceDir: File): Option[File] =
if (defines.isEmpty) None
else {
val template = resolveTemplate(defaultTemplateLocation)
val scriptBits = JavaAppAshScript.generateScript(defines, template)
val script = tmpDir / "tmp" / "bin" / name
IO.write(script, scriptBits)
// TODO - Better control over this!
script.setExecutable(true)
Some(script)
}

private def resolveTemplate(defaultTemplateLocation: File): URL = {
if (defaultTemplateLocation.exists)
defaultTemplateLocation.toURI.toURL
else
getClass.getResource(defaultTemplateLocation.getName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.typesafe.sbt.packager.archetypes

import java.net.URL

/**
* Constructs a bash script for running a java application.
*
* Makes use of the associated bash-template, with a few hooks
*
*/
object JavaAppAshScript {

private[this] def bashTemplateSource =
getClass.getResource("bash-template")

/**
* Creates the block of defines for a script.
*
* @param mainClass The required "main" method class we use to run the program.
* @param appClasspath A sequence of relative-locations (to the lib/ folder) of jars
* to include on the classpath.
* @param configFile An (optional) filename from which the script will read arguments.
* @param extras Any additional defines/commands that should be run in this script.
*/
def makeDefines(
mainClass: String,
appClasspath: Seq[String] = Seq("*"),
configFile: Option[String] = None,
extras: Seq[String] = Nil): Seq[String] =
Seq(mainClassDefine(mainClass)) ++
(configFile map configFileDefine).toSeq ++
Seq(makeClasspathDefine(appClasspath)) ++
extras

private def makeClasspathDefine(cp: Seq[String]): String = {
val fullString = cp map (n => "$lib_dir/" + n) mkString ":"
"app_classpath=\"" + fullString + "\"\n"
}
def generateScript(defines: Seq[String], template: URL = bashTemplateSource): String = {
val defineString = defines mkString "\n"
val replacements = Seq("template_declares" -> defineString)
TemplateWriter.generateScript(template, replacements)
}

def configFileDefine(configFile: String) =
"script_conf_file=\"%s\"" format (configFile)

def mainClassDefine(mainClass: String) = {
val jarPrefixed = """^\-jar (.*)""".r
val args = mainClass match {
case jarPrefixed(jarName) => Seq("-jar", jarName)
case className => Seq(className)
}
val quotedArgsSpaceSeparated = args.map(s => "\"" + s + "\"").mkString(" ")
"app_mainclass=%s\n" format (quotedArgsSpaceSeparated)
}

}
18 changes: 18 additions & 0 deletions src/sbt-test/ash/override-templates/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import scala.io.Source

enablePlugins(AshScriptPlugin)

name := "override-templates"

version := "0.1.0"

bashScriptTemplateLocation := baseDirectory.value / "custom-templates" / "custom-ash-template"

TaskKey[Unit]("run-check-ash") := {
val cwd = (stagingDirectory in Universal).value
val source = scala.io.Source.fromFile((cwd / "bin" / packageName.value).getAbsolutePath)
val contents = try source.getLines mkString "\n" finally source.close()
assert(contents contains "this is the custom bash template", "Bash template didn't contain the right text: \n" + contents)
assert(contents contains "app_mainclass=","Template didn't contain the right text: \n"+contents)
assert( !(contents contains "declare"),"Template didn't contain the right text: \n"+contents)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env sh

# this is the custom bash template

${{template_declares}}
1 change: 1 addition & 0 deletions src/sbt-test/ash/override-templates/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object MainApp extends App {
println("SUCCESS!")
}
3 changes: 3 additions & 0 deletions src/sbt-test/ash/override-templates/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Run the staging and check the script.
> stage
> run-check-ash
12 changes: 12 additions & 0 deletions src/sbt-test/ash/simple-app/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
enablePlugins(AshScriptPlugin)

name := "simple-app"

version := "0.1.0"

TaskKey[Unit]("run-check") := {
val cwd = (stagingDirectory in Universal).value
val cmd = Seq((cwd / "bin" / packageName.value).getAbsolutePath)
val output = Process(cmd, cwd).!!
assert(output contains "SUCCESS!", "Output didn't contain success: " + output)
}
1 change: 1 addition & 0 deletions src/sbt-test/ash/simple-app/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % sys.props("project.version"))
3 changes: 3 additions & 0 deletions src/sbt-test/ash/simple-app/src/main/scala/MainApp.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object MainApp extends App {
println("SUCCESS!")
}
3 changes: 3 additions & 0 deletions src/sbt-test/ash/simple-app/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Run the staging and check the script.
> stage
> run-check

0 comments on commit ea4f9cf

Please sign in to comment.