aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--stage1/Stage1Lib.scala90
-rw-r--r--stage2/BasicBuild.scala14
-rw-r--r--test/test.scala3
3 files changed, 64 insertions, 43 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){
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index baa6d26..98deddc 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -136,13 +136,15 @@ trait BaseBuild extends DependencyImplementation with BuildInterface with Trigge
)
}
- def runClass: String = lib.mainClasses ( compileTarget, classLoader(context.classLoaderCache) ) match {
- case f if f.length == 1 => f.head
- case f if f.length > 2 => System.err.println( " Multiple main classes defined in project." ); "None"
- case _ => "Main"
- }
+
+ def mainClasses: Seq[Class[_]] = compile.toSeq.flatMap( lib.mainClasses( _, classLoader(classLoaderCache) ) )
- def run: ExitCode = lib.runMainIfFound( runClass, context.args, classLoader(context.classLoaderCache) )
+ def runClass: Option[String] = lib.runClass( mainClasses ).map( _.getName )
+
+ def run: ExitCode = runClass.map( lib.runMain( _, context.args, classLoader(context.classLoaderCache) ) ).getOrElse{
+ logger.task( "No main class found for " ++ projectDirectory.string )
+ ExitCode.Success
+ }
def clean = {
lib.clean(
diff --git a/test/test.scala b/test/test.scala
index dfc35d0..8845528 100644
--- a/test/test.scala
+++ b/test/test.scala
@@ -1,4 +1,5 @@
-import cbt._
+package cbt
+package test
import java.util.concurrent.ConcurrentHashMap
import java.io.File
import java.nio.file._