.10.dp.source-code.boot.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of boot_2.10 Show documentation
Show all versions of boot_2.10 Show documentation
drx wraper around the sbt-launcher to support config file based launching
The newest version!
/*
Copyright 2010 Aaron J. Radke
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//TODO add mechanism to add java system properties so apps that need them set can find them
//TODO include the systemclass path of the luancher jar, that was not included (either in the classpath or the ng project)
//TODO make a fork that uses javaw
//TODO bt.kson option to launch a forked or bg process
//TODO run bt from a luanched ng cc.drx.Boot
//TODO make a bt.kson option lookup for a fast registry like lookup mechanism
//TODO make a custom ng boot that launches a cc.drx.ng.Server and friends server if not already launched
//TODO maybe revisit the trouble using xsbti.ServerMain (in the meantime decided to use ng server instead)
package cc.drx
import Repo.{Release,Org}
object Jansi{
def apply[A](body: => A):A = {
System.setProperty("java.awt.headless", "true") //prevent osx showing java
if(OS.kind == OS.Windows) org.fusesource.jansi.AnsiConsole.systemInstall()
body
}
}
object BootSbt{
def main(args:Array[String]):Unit = xsbt.boot.Boot.main(args) //TODO if this test ever works that means boots can be forwarded so make it work nice, if it doesn't try the xsbt.boot.Launch methods
}
object Boot{
def pp(width:Int, key:String, value:String):Unit = println(
s"${Color.Blue ansi key}${Color.Yellow ansi ":"}$value"
)
class Exit(val code: Int) extends xsbti.Exit
def classpathFrom(conf:xsbti.AppConfiguration):Classpath = classpathFrom(conf.provider)
def classpathFrom(provider:xsbti.AppProvider):Classpath = {
val cpScala = Classpath(provider.scalaProvider.jars map File.apply)
val cpBoot = Classpath(provider.mainClasspath map File.apply)
val cpLauncher = Classpath.system
cpScala ++ cpBoot ++ cpLauncher
}
case class AppId(org:String, prj:String, ver:String, main:String,
cross:String="Disabled",
extra:List[String]=Nil,
cp:Classpath=Classpath(),
scala:String="auto",
ctx:StringMap
) extends xsbti.ApplicationID {
def classpathExtra:Array[java.io.File] = cp.files.map{_.file}.toArray
def mainComponents = extra.toArray
def groupID = org
def name = prj
def version = ver
def mainClass = main
def crossVersioned:Boolean = crossVersionedValue != xsbti.CrossValue.Disabled
private def isCross:Boolean = crossVersionedValue != xsbti.CrossValue.Disabled
def crossVersionedValue:xsbti.CrossValue = xsbti.CrossValue.valueOf(cross.trim.toLowerCase.capitalize)
override def toString = {
s"$prj ($ver) from $org using main:$main"+
List(
if(isCross) Some(s"cross: $crossVersioned") else None,
if(cp.files.nonEmpty) Some(s"cp: ${cp.toString}") else None,
if(extra.nonEmpty) Some(s"extra: ${extra mkString ";"}") else None,
if(scala!="auto") Some(s"scala: $scala") else None
).flatten.map{"\n " + _}.mkString("")
}
}
case class AppConf(appId:AppId, args:Array[String], base:File){
override def toString = s"AppConf(appId:$appId args:${args mkString " "} base:$base)"
def run:xsbti.MainResult = appId.ctx.getString("lang") match {
case Some("shell") => {
import Implicit.ec
val shell = Shell(appId.main +: args, base)
println(shell)
val res = shell.run.blockWithoutWarning(1.yr)
new Boot.Exit(0)
}
case _ => {
new xsbti.Reboot{
def arguments = args//conf.arguments.drop(1)
def baseDirectory = base.file //conf.baseDirectory
def scalaVersion = appId.scala
def app = appId
}
}
}
/*
def start:xsbti.Server = {
new xsbti.Server{
def uri = new java.net.URI("sbt://localhost:9999") // FIXME setup an actual ip and port for the test
def awaitTermination = {
// val serverMain = provider.entryPoint.asSubclass(ServerMainClass).newInstance
run //TODO launch the main blocking class here
while(true){Thread.sleep(100)}
new Boot.Exit(0)
}
}
}
*/
}
private val javaVer = "java" + OS.java8.getOrElse("8","7") //auto detect java
private val scopes = List("scala","java","sbt")
/**find a boot conf from a key and launch it with a specified special args*/
def find(conf: xsbti.AppConfiguration, key:String, args:Array[String]):Option[AppConf] = {
val base = File(conf.baseDirectory)
val launchedScala = conf.provider.scalaProvider.version
val cpBoot = classpathFrom(conf)
// Print a message and the arguments to the application
val isVerbose = args contains "-v"
if(isVerbose){
println(
"Welcome to drx-boot. " +
Color.Grey.ansi("A context dependent launcher/build-tool/boot-loader.")
)
print(s"Looking for ${Color.Green ansi key wrap "*"} params (using scala:$launchedScala)")
}
val bootKeys = BootKeys.load //TODO add a timer to report the load time if in verbose mode
val ctxOption = bootKeys.getContext(key)
//--setup vars for launch mode
lazy val boot:Option[AppConf] = for(
ctx <- ctxOption;
org <- ctx.get[String]("org");
prj <- ctx.get[String]("prj");
ver <- ctx.get[String]("ver") orElse ctx.get[String]("ver."+javaVer);
main <- ctx.get[String]("main") //it must define a main method to be launched
) yield {
val cross = ctx.get[String]("cross") | "Disabled"
val scala:String = ctx.get[String]("scala") getOrElse "2.12.5" //orElse kson.get[String]("scala.scala.ver."+javaVer) getOrElse "2.12.2" //no auto scala, always enforce a specific version that is specified somewhere
// val extra:List[String] = ctx.get[String]("extra").map{_.trim.split("[\\s,:]+").toList} getOrElse Nil
val extra:List[String] = ctx.split[String]("extra","[\\s,:]+").toList
val cp:Classpath = Classpath(ctx.split[File]("cp").toList)
val debug:Boolean = ctx.getOrElse[Boolean]("debug", false)
val lang:Option[String] = ctx getString "lang"
val preArgs:Array[String] = ctx.split[String]("args","\\s+").toArray
val fork:Boolean = ctx.getOrElse[Boolean]("fork", false)
//val debug = false //FIXME make this debug BootInfo launcher set via a command line or property to remove this debug so the launcher will work
//--add custom jvm Props
// for((k,v) <- (ctx/'prop).toMap) sys.props(k) = v
sys.props ++= (ctx/'prop).toMap
if(fork || debug){
println(cpBoot.nice)
// hack the main with "cc.drx.BootInfo", and add the actual mainClass to the conf.args so the BootFork can launch the original mainClass and pull it off the argument stack
val altMain = if(debug) "cc.drx.BootInfo" else "cc.drx.BootFork"
val appId = new AppId(org,prj, ver, main=altMain, cross=cross, extra=extra, cp=cpBoot, scala=scala, ctx) //scala was 2.11.11 for forked inclusion??
AppConf(appId, Array(main) ++ preArgs ++ args, base)
}
else{
val appId = new AppId(org,prj, ver, main=main, cross=cross, extra=extra, cp=cp, scala=scala, ctx)
if(isVerbose) println(s"proxy to a app: $appId...")
AppConf(appId, preArgs ++ args, base)
}
}
//--select result to return (None on errors)
if(ctxOption.isEmpty){
//TODO furuther filter the bootKeys by luanchable types
println(s"${Red ansi "Error"}: Could not find launch parameters for key:${Orange ansi key}")
val alts = bootKeys.keys filter (_ soundsLike key)
if(alts.nonEmpty)
println(s" did you mean: " + alts.map{Green ansi _}.mkString(" | "))
else{
println("Available keys are:")
bootKeys.keys map (_ fit 20) grouped 5 map {_ mkString ""} foreach println
}
None
}
else if(boot.isEmpty){
println(s"${Red ansi "Error"}: invalid (missing) launch configuration for key:${Orange ansi key}")
None
}
else boot //return the first match
}
//nicer Rich wrappers
class ForkConf(val conf: xsbti.AppConfiguration){
val cp = Boot.classpathFrom(conf)
val thisArgs = conf.arguments
val thisMain = conf.provider.id.mainClass
val main = thisArgs.headOption getOrElse thisMain //the passed in
val base = File(conf.baseDirectory)
val args = thisArgs.drop(1)
val shell = Shell(List("java","-cp", cp.toString, main) ++ args) //TODO make a BootFork that does the forked app work
override def toString =
List(
Boot.pp(20, "BaseDirectory:",base.path),
Boot.pp(20, "Args:", args.mkString(" ")),
Boot.pp(20, "Main:",main),
Boot.pp(20, "Classpath:","...\n"+cp.nice.indent)
).mkString("\n")
}
}
class BootFork extends xsbti.AppMain{
def run(appConf: xsbti.AppConfiguration):xsbti.MainResult = Jansi{
println("#BootFork hijacked the boot to collect a claspath that can be forked.") //just print out the information
val conf = new Boot.ForkConf(appConf)
println(conf)
val res = conf.shell.fork //do the fork work
println(res)
new Boot.Exit(0)
}
}
class BootInfo extends xsbti.AppMain{
def run(appConf: xsbti.AppConfiguration):xsbti.MainResult = Jansi{
Timer("BootInfo"){
println("#BootInfo hijacked the boot for debug purposes.") //just print out the information
def pp(title:String, kvs:Map[String,String]):Unit = {
println("# " + Color.Green.ansi(title))
val maxWidth = kvs.keys.map{_.size}.max
for((k,v) <- kvs.toList.sortBy{_._1.toLowerCase}) Boot.pp(maxWidth, k, v)
}
//--list the available bootkeys
BootKeys.main(Array.empty[String])
//--list environment vars
pp("Environment variables", sys.env )
//--list jvm properties
pp("JVM properties", sys.props.toMap ) //to an immutable map
val conf = new Boot.ForkConf(appConf)
println(conf) //just print out the information
println(conf.shell) //just print out the information
new Boot.Exit(0)
}
}
}
class BootKeys(kson:Kson){
//--private
private lazy val rootPaths = for(line <- kson.lines if line.roots.nonEmpty) yield line.pathList //just the lines with a root key def
private lazy val scopeMap = rootPaths.map{p => p.head -> p.tail.reverse.mkString("/")}.toMap
def getContext(key:String):Option[StringMap.Scoped] = scopeMap.get(key).map(kson/_/key)
//--public
lazy val keys = rootPaths.map{_.head}.toSet //keys sorted in the order of the kson config file
def print(requestKeys:Seq[String]):Unit = {
val missingKeys = requestKeys.filterNot(scopeMap contains _)
//--print missing key warning
if(missingKeys.nonEmpty) {
for(miss <- missingKeys){
val alts = for(key <- keys if miss soundsLike key) yield key
println(s"no key found for `$miss`" )
if(alts.nonEmpty) println(" maybe try: " + alts.mkString(", ") )
}
}
//-- no missing keys
else{
for(key <- if(requestKeys.isEmpty) keys else requestKeys;
scope <- scopeMap get key; //lookup the scope just for the nice paren printing
ctx <- getContext(key);
(org,prj,ver) <- ctx.get("org","prj","ver")
) {
val scopeParen = "("+scope+")"
println(f"$key%-20s $scopeParen%-25s $prj%-25s $ver%-10s $org%-20s")
}
}
}
def dep(key:String):Option[(String,String,String)] = {
for(
scope <- scopeMap get key; //lookup the scope just for the nice paren printing
ctx <- getContext(key);
orgPrjVer <- ctx.get("org","prj","ver")
) yield orgPrjVer
}
}
//--object BootKeys
object BootKeys{
def findBootFile:Option[File] = {
val defaultFile = File.cwd/".bt" orElse
File.cwd/".bt.kson" orElse
File.cwd/"bt.kson" orElse
File.cwd/".bt.kson" orElse
File.home/".bt" orElse
File.home/".bt.kson" orElse
File.home/"bt.kson"
// println(s"in ${if(defaultFile.isFile) defaultFile else "default"} ")
if(defaultFile.isFile) Some(defaultFile) else None
}
def loadConfig:Kson = {
val ksonDefault = Input.resource(file"bt.kson").map{Kson.apply} getOrElse Kson.empty //should always be there
val ksonBootFile = findBootFile.map{Kson(_)} getOrElse Kson.empty
ksonDefault ++ ksonBootFile
}
def load:BootKeys = new BootKeys(loadConfig)
def apply(kson:Kson):BootKeys = new BootKeys(kson)
def main(args:Array[String]):Unit = Jansi{
println("Boot config bt.kson keys")
val bootKeys = Timer("load boot keys"){BootKeys.load}
Timer("find/list keys"){ bootKeys print args.toList } //TODO use fuzzy finding match for missing keys
Console.flush
1.s.sleep//sleep to prevent early stream is cut before finsihing
}
}
class Boot extends xsbti.AppMain{
def run(conf: xsbti.AppConfiguration):xsbti.MainResult = Jansi{
val key = conf.arguments.headOption getOrElse "sbt" //TODO add an auto boot detector app that checks if it is an sbt or gradle or mvn project or undefined project
// println(s"run with $key")
val args = conf.arguments.drop(1)
val res = Boot.find(conf,key,args).map{_.run}.getOrElse{new Boot.Exit(0)}
//TODO add console/shell runner if empty args???
res
}
}
class BootTest extends xsbti.AppMain{
def run(conf: xsbti.AppConfiguration):xsbti.MainResult = {
// xsbt.boot.Boot.main(Array())//Array("@drx.boot"))
new Boot.Exit(0)
}
}
object BootTestPlain extends App{
println("Yeah! luanched boottest.")
// import xsbt.boot._
val baseDir:java.io.File = File(".")
xsbt.boot.Boot.main(Array())//Array("@drx.boot"))
// val repos:List[Repository.Repository] = List()
// val luancher = Launcher.apply(File("."))
}
/*
object BootTest{
def main(args:Array[String]):Unit = {
import xsbt.boot._
val launchConfig = {
val scalaVersion = new Explicit("2.12.5")
val ivyConfig:IvyOptions = ???
val app:Application = ???
val boot:BootSetup = ???
val logging:Logging = ???
val appProperties:List[AppProperty] = Nil
val serverConfig:Option[ServerConfiguration] = None
new LaunchConfiguration(scalaVersion, ivyConfig, app, boot, logging, appProperties, serverConfig)
}
val launcher = xsbt.boot.Launcher.apply(launchConfig)
val launcher = new Launch(java.io.File("."), java.o
// val runConfig = makeRunConfig(currentDirectory, config, newArgs2)
// Luanch.run(launcher)(runConfig)
}
}
*/
/*
//run a shell command (passing through system independent; i.e. fixes for windows)
object BootShell{
def main(args:Array[String]):Unit = Jansi{
import Implicit.ec
val shell = Shell(args)
val res = shell.run.blockWithoutWarning(1.yr)
Console.flush
// res getOrElse 1 //1 is an error
}
}
*/
© 2015 - 2025 Weber Informatics LLC | Privacy Policy