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

Change startup script name generation #1020

Merged
merged 12 commits into from
Jan 8, 2018
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.typesafe.sbt.packager.archetypes.scripts

import java.io.File
import java.net.URL

import com.typesafe.sbt.SbtNativePackager.Universal
import com.typesafe.sbt.packager.Keys._
Expand All @@ -16,7 +15,7 @@ import sbt._
* [[com.typesafe.sbt.packager.archetypes.JavaAppPackaging]].
*
*/
object BashStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
object BashStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator with CommonStartScriptGenerator {

/**
* Name of the bash template if user wants to provide custom one
Expand All @@ -26,27 +25,32 @@ object BashStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
/**
* Name of the bash forwarder template if user wants to provide custom one
*/
val bashForwarderTemplate = "bash-forwarder-template"
override protected[this] val forwarderTemplateName = "bash-forwarder-template"

/**
* Location for the application.ini file used by the bash script to load initialization parameters for jvm and app
*/
val appIniLocation = "${app_home}/../conf/application.ini"

/**
* Script destination in final package
*/
val scriptTargetFolder = "bin"
override protected[this] val scriptSuffix: String = ""
override protected[this] val eol: String = "\n"
override protected[this] val keySurround: String => String = TemplateWriter.bashFriendlyKeySurround
override protected[this] val executableBitValue: Boolean = true

override val requires = JavaAppPackaging
override val trigger = AllRequirements

object autoImport extends BashStartScriptKeys

private[this] case class BashScriptConfig(executableScriptName: String,
scriptClasspath: Seq[String],
bashScriptReplacements: Seq[(String, String)],
bashScriptTemplateLocation: File)
protected[this] case class BashScriptConfig(override val executableScriptName: String,
override val scriptClasspath: Seq[String],
override val replacements: Seq[(String, String)],
override val templateLocation: File)
extends ScriptConfig {
override def withScriptName(scriptName: String): BashScriptConfig = copy(executableScriptName = scriptName)
}

override protected[this] type SpecializedScriptConfig = BashScriptConfig

override def projectSettings: Seq[Setting[_]] = Seq(
bashScriptTemplateLocation := (sourceDirectory.value / "templates" / bashTemplate),
Expand All @@ -69,8 +73,8 @@ object BashStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
BashScriptConfig(
executableScriptName = executableScriptName.value,
scriptClasspath = (scriptClasspath in bashScriptDefines).value,
bashScriptReplacements = bashScriptReplacements.value,
bashScriptTemplateLocation = bashScriptTemplateLocation.value
replacements = bashScriptReplacements.value,
templateLocation = bashScriptTemplateLocation.value
),
(mainClass in (Compile, bashScriptDefines)).value,
(discoveredMainClasses in Compile).value,
Expand All @@ -85,44 +89,6 @@ object BashStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
Seq("template_declares" -> defineString)
}

private[this] def generateStartScripts(config: BashScriptConfig,
mainClass: Option[String],
discoveredMainClasses: Seq[String],
targetDir: File,
log: Logger): Seq[(File, String)] =
StartScriptMainClassConfig.from(mainClass, discoveredMainClasses) match {
case NoMain =>
log.warn("You have no main class in your project. No start script will be generated.")
Seq.empty
case SingleMain(main) =>
Seq(MainScript(main, config, targetDir, Seq(main)) -> s"$scriptTargetFolder/${config.executableScriptName}")
case MultipleMains(mains) =>
generateMainScripts(mains, config, targetDir)
case ExplicitMainWithAdditional(main, additional) =>
(MainScript(main, config, targetDir, discoveredMainClasses) -> s"$scriptTargetFolder/${config.executableScriptName}") +:
ForwarderScripts(config.executableScriptName, additional, targetDir)
}

private[this] def generateMainScripts(discoveredMainClasses: Seq[String],
config: BashScriptConfig,
targetDir: File): Seq[(File, String)] =
discoveredMainClasses.map { qualifiedClassName =>
val bashConfig =
config.copy(executableScriptName = makeScriptName(qualifiedClassName))
MainScript(qualifiedClassName, bashConfig, targetDir, discoveredMainClasses) -> s"$scriptTargetFolder/${bashConfig.executableScriptName}"
}

private[this] def makeScriptName(qualifiedClassName: String): String = {
val clazz = qualifiedClassName.split("\\.").last

val lowerCased = clazz.drop(1).flatMap {
case c if c.isUpper => Seq('-', c.toLower)
case c => Seq(c)
}

clazz(0).toLower +: lowerCased
}

/**
* @param path that could be relative to app_home
* @return path relative to app_home
Expand Down Expand Up @@ -157,56 +123,16 @@ object BashStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
"declare -r script_conf_file=\"%s\"" format configFile
}

object MainScript {

/**
*
* @param mainClass - Main class added to the java command
* @param config - Config data for this script
* @param targetDir - Target directory for this script
* @return File pointing to the created main script
*/
def apply(mainClass: String, config: BashScriptConfig, targetDir: File, mainClasses: Seq[String]): File = {
val template = resolveTemplate(config.bashScriptTemplateLocation)
val replacements = Seq(
"app_mainclass" -> mainClass,
"available_main_classes" -> usageMainClassReplacement(mainClasses)
) ++ config.bashScriptReplacements

val scriptContent = TemplateWriter.generateScript(template, replacements)
val script = targetDir / "scripts" / config.executableScriptName
IO.write(script, scriptContent)
// TODO - Better control over this!
script.setExecutable(true)
script
}

private[this] def usageMainClassReplacement(mainClasses: Seq[String]): String =
if (mainClasses.nonEmpty)
mainClasses.mkString("Available main classes:\n\t", "\n\t", "")
else
""

private[this] def resolveTemplate(defaultTemplateLocation: File): URL =
if (defaultTemplateLocation.exists) defaultTemplateLocation.toURI.toURL
else getClass.getResource(defaultTemplateLocation.getName)
}

object ForwarderScripts {
def apply(executableScriptName: String, discoveredMainClasses: Seq[String], targetDir: File): Seq[(File, String)] = {
val tmp = targetDir / "scripts"
val forwarderTemplate = getClass.getResource(bashForwarderTemplate)
discoveredMainClasses.map { qualifiedClassName =>
val clazz = makeScriptName(qualifiedClassName)
val file = tmp / clazz

val replacements = Seq("startScript" -> executableScriptName, "qualifiedClassName" -> qualifiedClassName)
val scriptContent = TemplateWriter.generateScript(forwarderTemplate, replacements)

IO.write(file, scriptContent)
file.setExecutable(true)
file -> s"bin/$clazz"
}
}
}
private[this] def usageMainClassReplacement(mainClasses: Seq[String]): String =
if (mainClasses.nonEmpty)
mainClasses.mkString("Available main classes:\n\t", "\n\t", "")
else
""

override protected[this] def createReplacementsForMainScript(
mainClass: String,
mainClasses: Seq[String],
config: SpecializedScriptConfig
): Seq[(String, String)] =
Seq("app_mainclass" -> mainClass, "available_main_classes" -> usageMainClassReplacement(mainClasses)) ++ config.replacements
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.typesafe.sbt.packager.archetypes.scripts

import java.io.File
import java.net.URL

import com.typesafe.sbt.SbtNativePackager.Universal
import com.typesafe.sbt.packager.Keys._
Expand All @@ -17,7 +16,7 @@ import sbt._
* [[com.typesafe.sbt.packager.archetypes.JavaAppPackaging]].
*
*/
object BatStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
object BatStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator with CommonStartScriptGenerator {

/**
* Name of the bat template if user wants to provide custom one
Expand All @@ -27,30 +26,35 @@ object BatStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
/**
* Name of the bat forwarder template if user wants to provide custom one
*/
val batForwarderTemplate = "bat-forwarder-template"

/**
* Script destination in final package
*/
val scriptTargetFolder = "bin"
override protected[this] val forwarderTemplateName = "bat-forwarder-template"

/**
* Location for the application.ini file used by the bat script to load initialization parameters for jvm and app
*/
val appIniLocation = "%APP_HOME%\\conf\\application.ini"

override protected[this] val scriptSuffix = ".bat"
override protected[this] val eol: String = "\r\n"
override protected[this] val keySurround: String => String = TemplateWriter.batFriendlyKeySurround
override protected[this] val executableBitValue: Boolean = false

override val requires = JavaAppPackaging
override val trigger = AllRequirements

object autoImport extends BatStartScriptKeys
import autoImport._

private[this] case class BatScriptConfig(executableScriptName: String,
scriptClasspath: Seq[String],
configLocation: Option[String],
extraDefines: Seq[String],
replacements: Seq[(String, String)],
batScriptTemplateLocation: File)
protected[this] case class BatScriptConfig(override val executableScriptName: String,
override val scriptClasspath: Seq[String],
configLocation: Option[String],
extraDefines: Seq[String],
override val replacements: Seq[(String, String)],
override val templateLocation: File)
extends ScriptConfig {
override def withScriptName(scriptName: String): BatScriptConfig = copy(executableScriptName = scriptName)
}

override protected[this] type SpecializedScriptConfig = BatScriptConfig

override def projectSettings: Seq[Setting[_]] = Seq(
batScriptTemplateLocation := (sourceDirectory.value / "templates" / batTemplate),
Expand All @@ -67,12 +71,12 @@ object BatStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
),
makeBatScripts := generateStartScripts(
BatScriptConfig(
executableScriptName = s"${executableScriptName.value}.bat",
executableScriptName = executableScriptName.value,
scriptClasspath = (scriptClasspath in batScriptReplacements).value,
configLocation = batScriptConfigLocation.value,
extraDefines = batScriptExtraDefines.value,
replacements = batScriptReplacements.value,
batScriptTemplateLocation = batScriptTemplateLocation.value
templateLocation = batScriptTemplateLocation.value
),
(mainClass in (Compile, batScriptReplacements)).value,
(discoveredMainClasses in Compile).value,
Expand All @@ -82,43 +86,6 @@ object BatStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
mappings in Universal ++= makeBatScripts.value
)

private[this] def generateStartScripts(config: BatScriptConfig,
mainClass: Option[String],
discoveredMainClasses: Seq[String],
targetDir: File,
log: Logger): Seq[(File, String)] =
StartScriptMainClassConfig.from(mainClass, discoveredMainClasses) match {
case NoMain =>
log.warn("You have no main class in your project. No start script will be generated.")
Seq.empty
case SingleMain(main) =>
Seq(MainScript(main, config, targetDir) -> s"$scriptTargetFolder/${config.executableScriptName}")
case MultipleMains(mains) =>
generateMainScripts(discoveredMainClasses, config, targetDir)
case ExplicitMainWithAdditional(main, additional) =>
(MainScript(main, config, targetDir) -> s"$scriptTargetFolder/${config.executableScriptName}") +:
ForwarderScripts(config.executableScriptName, additional, targetDir)
}

private[this] def generateMainScripts(discoveredMainClasses: Seq[String],
config: BatScriptConfig,
targetDir: File): Seq[(File, String)] =
discoveredMainClasses.map { qualifiedClassName =>
val batConfig = config.copy(executableScriptName = makeScriptName(qualifiedClassName))
MainScript(qualifiedClassName, batConfig, targetDir) -> s"$scriptTargetFolder/${batConfig.executableScriptName}"
}

private[this] def makeScriptName(qualifiedClassName: String): String = {
val clazz = qualifiedClassName.split("\\.").last

val lowerCased = clazz.drop(1).flatMap {
case c if c.isUpper => Seq('-', c.toLower)
case c => Seq(c)
}

clazz(0).toLower + lowerCased + ".bat"
}

/**
* @param path that could be relative to APP_HOME
* @return path relative to APP_HOME
Expand Down Expand Up @@ -173,47 +140,11 @@ object BatStartScriptPlugin extends AutoPlugin with ApplicationIniGenerator {
}
}

object MainScript {

/**
*
* @param mainClass - Main class added to the java command
* @param config - Config data for this script
* @param targetDir - Target directory for this script
* @return File pointing to the created main script
*/
def apply(mainClass: String, config: BatScriptConfig, targetDir: File): File = {
val template = resolveTemplate(config.batScriptTemplateLocation)
val replacements = config.replacements :+ Replacements.appDefines(mainClass, config, config.replacements)
val scriptContent =
TemplateWriter.generateScript(template, replacements, "\r\n", TemplateWriter.batFriendlyKeySurround)
val script = targetDir / "scripts" / config.executableScriptName
IO.write(script, scriptContent)
script
}

private[this] def resolveTemplate(defaultTemplateLocation: File): URL =
if (defaultTemplateLocation.exists) defaultTemplateLocation.toURI.toURL
else getClass.getResource(defaultTemplateLocation.getName)

}

object ForwarderScripts {
def apply(executableScriptName: String, discoveredMainClasses: Seq[String], targetDir: File): Seq[(File, String)] = {
val tmp = targetDir / "scripts"
val forwarderTemplate = getClass.getResource(batForwarderTemplate)
discoveredMainClasses.map { qualifiedClassName =>
val scriptName = makeScriptName(qualifiedClassName)
val file = tmp / scriptName

val replacements = Seq("startScript" -> executableScriptName, "qualifiedClassName" -> qualifiedClassName)
val scriptContent =
TemplateWriter.generateScript(forwarderTemplate, replacements, "\r\n", TemplateWriter.batFriendlyKeySurround)

IO.write(file, scriptContent)
file -> s"bin/$scriptName"
}
}
override protected[this] def createReplacementsForMainScript(
mainClass: String,
mainClasses: Seq[String],
config: SpecializedScriptConfig
): Seq[(String, String)] =
config.replacements :+ Replacements.appDefines(mainClass, config, config.replacements)

}
}
Loading