diff options
Diffstat (limited to 'stage1/Stage1Lib.scala')
-rw-r--r-- | stage1/Stage1Lib.scala | 173 |
1 files changed, 60 insertions, 113 deletions
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala index 565a06c..3ffc878 100644 --- a/stage1/Stage1Lib.scala +++ b/stage1/Stage1Lib.scala @@ -11,31 +11,11 @@ import java.security._ import java.util.{Set=>_,Map=>_,List=>_,_} import javax.xml.bind.annotation.adapters.HexBinaryAdapter -// CLI interop -case class ExitCode(integer: Int){ - def ||( other: => ExitCode ) = if( this == ExitCode.Success ) this else other - def &&( other: => ExitCode ) = if( this != ExitCode.Success ) this else other -} -object ExitCode{ - val Success = ExitCode(0) - val Failure = ExitCode(1) -} - -object CatchTrappedExitCode{ - def unapply(e: Throwable): Option[ExitCode] = { - Option(e) flatMap { - case i: InvocationTargetException => unapply(i.getTargetException) - case e if TrapSecurityManager.isTrappedExit(e) => Some( ExitCode(TrapSecurityManager.exitCode(e)) ) - case _ => None - } - } -} - -class BaseLib{ - def realpath(name: File) = new File(java.nio.file.Paths.get(name.getAbsolutePath).normalize.toString) -} - -class Stage1Lib( logger: Logger ) extends BaseLib{ +class Stage1Lib( logger: Logger ) extends + _root_.cbt.common_1.Module with + _root_.cbt.reflect.Module with + _root_.cbt.file.Module +{ lib => implicit protected val implicitLogger: Logger = logger @@ -99,24 +79,28 @@ class Stage1Lib( logger: Logger ) extends BaseLib{ } } + /* // ========== compilation / execution ========== // TODO: move classLoader first - def runMain( cls: String, args: Seq[String], classLoader: ClassLoader, fakeInstance: Boolean = false ): ExitCode = { + def runMain( className: String, args: Seq[String], classLoader: ClassLoader ): ExitCode = { import java.lang.reflect.Modifier - logger.run(s"Running $cls.main($args) with classLoader: " ++ classLoader.toString) + logger.run(s"Running $className.main($args) with classLoader: " ++ classLoader.toString) trapExitCode{ - val c = classLoader.loadClass(cls) - val m = c.getMethod( "main", classOf[Array[String]] ) - val instance = - if(!fakeInstance) null else c.newInstance - assert( - fakeInstance || (m.getModifiers & java.lang.reflect.Modifier.STATIC) > 0, - "Cannot run non-static method " ++ cls+".main" - ) - m.invoke( instance, args.toArray.asInstanceOf[AnyRef] ) + /* + val cls = classLoader.loadClass(className) + discoverCbtMain( cls ) orElse discoverMain( cls ) getOrElse ( + throw new NoSuchMethodException( "No main method found in " ++ cbt ) + ).apply( arg.toVector )*/ ExitCode.Success } } + */ + + def discoverCbtMainForced( cls: Class[_] ): cbt.reflect.StaticMethod[Context, ExitCode] = + discoverStaticMethodForced[Context, ExitCode]( cls, "cbtMain" ) + + def discoverCbtMain( cls: Class[_] ): Option[cbt.reflect.StaticMethod[Context, ExitCode]] = + discoverStaticMethod[Context, ExitCode]( cls, "cbtMain" ) /** 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] = { @@ -149,51 +133,10 @@ class Stage1Lib( logger: Logger ) extends BaseLib{ } /** interactively pick one main class */ - def runClass( mainClasses: Seq[Class[_]] ): Option[Class[_]] = { + def pickClass( mainClasses: Seq[Class[_]] ): Option[Class[_]] = { pickOne( "Which one do you want to run?", mainClasses )( _.toString ) } - /** Given a directory corresponding to the root package, iterate - the names of all classes derived from the class files found */ - def iterateClassNames( classesRootDirectory: File ): Seq[String] = - classesRootDirectory - .listRecursive - .filter(_.isFile) - .map(_.getPath) - .collect{ - // no $ to avoid inner classes - case path if !path.contains("$") && path.endsWith(".class") => - path.stripSuffix(".class") - .stripPrefix(classesRootDirectory.getPath) - .stripPrefix(File.separator) // 1 for the slash - .replace(File.separator, ".") - } - - /** ignoreMissingClasses allows ignoring other classes root directories which are subdirectories of this one */ - def iterateClasses( classesRootDirectory: File, classLoader: ClassLoader, ignoreMissingClasses: Boolean ) = - iterateClassNames(classesRootDirectory).map{ name => - try{ - classLoader.loadClass(name) - } catch { - case e: ClassNotFoundException if ignoreMissingClasses => null - case e: NoClassDefFoundError if ignoreMissingClasses => null - } - }.filterNot(ignoreMissingClasses && _ == null) - - def mainClasses( classesRootDirectory: File, classLoader: ClassLoader ): Seq[Class[_]] = { - val arrayClass = classOf[Array[String]] - val unitClass = classOf[Unit] - - iterateClasses( classesRootDirectory, classLoader, true ).filter( c => - !c.isInterface && - c.getDeclaredMethods().exists( m => - m.getName == "main" - && m.getParameterTypes.toList == List(arrayClass) - && m.getReturnType == unitClass - ) - ) - } - implicit class ClassLoaderExtensions(classLoader: ClassLoader){ def canLoad(className: String) = { try{ @@ -322,8 +265,9 @@ ${sourceFiles.sorted.mkString(" \\\n")} } } } - def redirectOutToErr[T](code: => T): T = { - val ( out, err ) = try{ + + def getOutErr: (ThreadLocal[PrintStream], ThreadLocal[PrintStream]) = + try{ // trying nailgun's System.our/err wrapper val field = System.out.getClass.getDeclaredField("streams") assert(System.out.getClass.getName == "com.martiansoftware.nailgun.ThreadLocalPrintStream") @@ -339,8 +283,8 @@ ${sourceFiles.sorted.mkString(" \\\n")} field.setAccessible(true) val outStream = field.get(System.out) val errStream = field.get(System.err) - assert(outStream.getClass.getName == "cbt.ThreadLocalOutputStream") - assert(errStream.getClass.getName == "cbt.ThreadLocalOutputStream") + assert(outStream.getClass.getName == "cbt.ThreadLocalOutputStream", outStream.getClass.getName) + assert(errStream.getClass.getName == "cbt.ThreadLocalOutputStream", errStream.getClass.getName) val field2 = outStream.getClass.getDeclaredField("threadLocal") field2.setAccessible(true) val out = field2.get(outStream).asInstanceOf[ThreadLocal[PrintStream]] @@ -348,6 +292,8 @@ ${sourceFiles.sorted.mkString(" \\\n")} ( out, err ) } + def redirectOutToErr[T](code: => T): T = { + val ( out, err ) = getOutErr val oldOut: PrintStream = out.get out.set( err.get: PrintStream ) val res = code @@ -355,21 +301,6 @@ ${sourceFiles.sorted.mkString(" \\\n")} res } - def trapExitCodeOrValue[T]( result: => T ): Either[ExitCode,T] = { - val trapExitCodeBefore = TrapSecurityManager.trapExitCode().get - try{ - TrapSecurityManager.trapExitCode().set(true) - Right( result ) - } catch { - case CatchTrappedExitCode(exitCode) => - logger.stage1(s"caught exit code $exitCode") - Left( exitCode ) - } finally { - TrapSecurityManager.trapExitCode().set(trapExitCodeBefore) - } - } - - def trapExitCode( code: => ExitCode ): ExitCode = trapExitCodeOrValue(code).merge def ScalaDependency( groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none, @@ -379,23 +310,39 @@ ${sourceFiles.sorted.mkString(" \\\n")} groupId, artifactId ++ "_" ++ scalaMajorVersion, version, classifier, verifyHash ) - def cacheOnDisk[T] - ( cbtLastModified: Long, cacheFile: File ) + def cacheOnDisk[T,J <: AnyRef : scala.reflect.ClassTag] + ( cbtLastModified: Long, cacheFile: File, persistentCache: java.util.Map[AnyRef,AnyRef] ) ( deserialize: String => T ) ( serialize: T => String ) - ( compute: => Seq[T] ) = { - if(cacheFile.exists && cacheFile.lastModified > cbtLastModified ){ - import collection.JavaConversions._ - Files - .readAllLines( cacheFile.toPath, StandardCharsets.UTF_8 ) - .toStream - .map(deserialize) - } else { - val result = compute - val string = result.map(serialize).mkString("\n") - write(cacheFile, string) - result - } + ( dejavafy: J => Seq[T] ) + ( javafy: Seq[T] => J ) + ( compute: => Seq[T] ): Seq[T] = { + val key = "cacheOnDisk:" + cacheFile + Option( persistentCache.get(key) ).map( + _.asInstanceOf[Array[AnyRef]] + ).map{ + case Array(time: java.lang.Long, javafied: J) => (time, javafied) + }.filter( _._1 > cbtLastModified ) + .map( _._2 ) + .map( dejavafy ) + .orElse{ + (cacheFile.exists && cacheFile.lastModified > cbtLastModified).option{ + import collection.JavaConversions._ + val v = Files + .readAllLines( cacheFile.toPath, StandardCharsets.UTF_8 ) + .toStream + .map( deserialize ) + persistentCache.put(key, Array(System.currentTimeMillis:java.lang.Long, javafy(v))) + v + } + }.getOrElse{ + val result = compute + val strings = result.map(serialize) + val string = strings.mkString("\n") + write(cacheFile, string) + persistentCache.put(key, Array(System.currentTimeMillis:java.lang.Long, javafy(result))) + result + } } def dependencyTreeRecursion(root: Dependency, indent: Int = 0): String = ( @@ -444,7 +391,7 @@ ${sourceFiles.sorted.mkString(" \\\n")} def classLoaderRecursion( dependency: Dependency, latest: Map[(String,String),Dependency])(implicit transientCache: java.util.Map[AnyRef,AnyRef], cache: ClassLoaderCache): ClassLoader = { // FIXME: shouldn't we be using KeyLockedLazyCache instead of hashmap directly here? - val dependencies = dependency.dependencies + val dependencies = dependency.dependencies.toVector val dependencyClassLoader: ClassLoader = { if( dependency.dependencies.isEmpty ){ NailgunLauncher.jdkClassLoader |