aboutsummaryrefslogtreecommitdiff
path: root/stage1/Stage1Lib.scala
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2016-10-02 11:52:13 -0400
committerChristopher Vogt <oss.nsp@cvogt.org>2016-10-02 13:39:53 -0400
commitdde9ac16434e7ddb7f24504c864c78a87e043553 (patch)
tree11e06767cbf07ff1717f3ed735dea6f4520a4c21 /stage1/Stage1Lib.scala
parent6b82f1ec42222b7e757ed8b79d56f30aabae96e6 (diff)
downloadcbt-dde9ac16434e7ddb7f24504c864c78a87e043553.tar.gz
cbt-dde9ac16434e7ddb7f24504c864c78a87e043553.tar.bz2
cbt-dde9ac16434e7ddb7f24504c864c78a87e043553.zip
clean up main class discovery mechanism and offer interactive choice
if multiple main classes are found
Diffstat (limited to 'stage1/Stage1Lib.scala')
-rw-r--r--stage1/Stage1Lib.scala90
1 files changed, 54 insertions, 36 deletions
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala
index 3a1fe45..5e65018 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -8,7 +8,7 @@ import java.nio.file._
import java.nio.file.attribute.FileTime
import javax.tools._
import java.security._
-import java.util.{Set=>_,Map=>_,_}
+import java.util.{Set=>_,Map=>_,List=>_,_}
import java.util.concurrent.ConcurrentHashMap
import javax.xml.bind.annotation.adapters.HexBinaryAdapter
@@ -95,12 +95,6 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
// ========== compilation / execution ==========
- def runMainIfFound(cls: String, args: Seq[String], classLoader: ClassLoader ): ExitCode = {
- if( classLoader.canLoad(cls) ){
- runMain(cls, args, classLoader )
- } else ExitCode.Success
- }
-
def runMain( cls: String, args: Seq[String], classLoader: ClassLoader, fakeInstance: Boolean = false ): ExitCode = {
import java.lang.reflect.Modifier
logger.lib(s"Running $cls.main($args) with classLoader: " ++ classLoader.toString)
@@ -118,41 +112,65 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
}
}
- def mainClasses(compileTarget : File, classLoader : ClassLoader): Seq[String] = {
- val targetLength = compileTarget.toString.length + 1 // compile target and slash
-
- def className(file : File) : String = {
- file.toString.dropRight(".class".length).drop(targetLength).replace(File.separator, ".")
- }
-
- /* for packaged applications */
- def retrieveFiles(file: File): Seq[File] = {
- file.listFiles.toSeq.map(currFile => {
- if (currFile.isDirectory) retrieveFiles(currFile)
- else Seq(currFile)
+ /** shows an interactive dialogue in the shell asking the user to pick one of many choices */
+ def pickOne[T]( msg: String, choices: Seq[T] )( show: T => String ): Option[T] = {
+ if(choices.size == 0) None else if(choices.size == 1) Some(choices.head) else {
+ Option(System.console).map{
+ console =>
+ val indexedChoices: Map[Int, T] = choices.zipWithIndex.toMap.mapValues(_+1).map(_.swap)
+ System.err.println(
+ indexedChoices.map{ case (index,choice) => s"[${index}] "++show(choice)}.mkString("\n")
+ )
+ val range = s"1 - ${indexedChoices.size}"
+ System.err.println()
+ System.err.println( msg ++ " [" ++ range ++ "] " )
+ val answer = console.readLine()
+ val choice = try{
+ Some(Integer.parseInt(answer))
+ }catch{
+ case e:java.lang.NumberFormatException => None
}
- ).flatten
- }
- def classesWithMain(target : File, classLoader : ClassLoader): Seq[File] = {
- def hasMainClass(file : File) : Boolean = {
- val name = className(file)
- val inspectClass = classLoader.loadClass(name)
- inspectClass.getDeclaredMethods().map(_.getName).contains("main")
+ choice.flatMap(indexedChoices.get).orElse{
+ System.err.println("Not in range "++range)
+ None
+ }
+ }.getOrElse{
+ System.err.println("Using '"++show(choices.head)++"' because System.console() == null. Use `cbt direct <task>` or see https://github.com/cvogt/cbt/issues/236")
+ None
}
-
- // FIXME: Check if argument list has exactly one element of type Array[String]
- def isInnerClass(file : File) : Boolean = file.toString.contains("$")
- val files = retrieveFiles(target)
-
- val classFiles = files.filter(file => file.toString.endsWith(".class") && !isInnerClass(file)).toSeq
-
- classFiles.filter(hasMainClass(_))
}
+ }
- val hasMain = classesWithMain(compileTarget, classLoader)
+ /** interactively pick one main class */
+ def runClass( mainClasses: Seq[Class[_]] ): Option[Class[_]] = {
+ pickOne( "Which one do you want to run?", mainClasses )( _.toString )
+ }
- hasMain.map(className(_))
+ def mainClasses( targetDirectory: File, classLoader : ClassLoader ): Seq[Class[_]] = {
+ val arrayClass = classOf[Array[String]]
+ val unitClass = classOf[Unit]
+
+ listFilesRecursive(targetDirectory)
+ .filter(_.isFile)
+ .map(_.getPath)
+ .collect{
+ // no $ to avoid inner classes
+ case path if !path.contains("$") && path.endsWith(".class") =>
+ classLoader.loadClass(
+ path
+ .stripSuffix(".class")
+ .stripPrefix(targetDirectory.getPath)
+ .stripPrefix(File.separator) // 1 for the slash
+ .replace(File.separator, ".")
+ )
+ }.filter(
+ _.getDeclaredMethods().exists( m =>
+ m.getName == "main"
+ && m.getParameterTypes.toList == List(arrayClass)
+ && m.getReturnType == unitClass
+ )
+ )
}
implicit class ClassLoaderExtensions(classLoader: ClassLoader){