aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Christopher Vogt <oss.nsp@cvogt.org>2016-04-07 00:37:20 -0400
committerJan Christopher Vogt <oss.nsp@cvogt.org>2016-04-07 00:37:20 -0400
commit0ef27a7d8cabd0dfe4009b09481566d3d02a76c6 (patch)
tree43f0cbea4fa66414f59763b589d029fd43cb7e6b
parent2d1a51d64aaca9ad00057a5bb822a50158b67429 (diff)
parentce8bab3856ec8755fb3b99be324f090770ddfe1f (diff)
downloadcbt-0ef27a7d8cabd0dfe4009b09481566d3d02a76c6.tar.gz
cbt-0ef27a7d8cabd0dfe4009b09481566d3d02a76c6.tar.bz2
cbt-0ef27a7d8cabd0dfe4009b09481566d3d02a76c6.zip
Merge pull request #102 from cvogt/chris
More classloading related fixes
-rw-r--r--TODO.txt1
-rw-r--r--stage1/ClassLoaderCache.scala12
-rw-r--r--stage1/Stage1.scala14
-rw-r--r--stage1/Stage1Lib.scala2
-rw-r--r--stage1/logger.scala2
-rw-r--r--stage1/resolver.scala63
-rw-r--r--stage2/AdminStage2.scala2
-rw-r--r--stage2/AdminTasks.scala6
-rw-r--r--stage2/BasicBuild.scala6
-rw-r--r--stage2/BuildBuild.scala24
-rw-r--r--stage2/BuildDependency.scala4
-rw-r--r--stage2/GitDependency.scala2
-rw-r--r--stage2/Lib.scala18
-rw-r--r--stage2/Stage2.scala7
-rw-r--r--test/test.scala2
15 files changed, 84 insertions, 81 deletions
diff --git a/TODO.txt b/TODO.txt
index 3da49f5..b6f4c15 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -3,7 +3,6 @@ TODO:
- improve logging
- immediate features
- - maybe rename context.cwd
- fix main project main method being run during tests
- investigate and solve multiple compilations of the same SourceDependency Build. Maybe introduce global Build map.
diff --git a/stage1/ClassLoaderCache.scala b/stage1/ClassLoaderCache.scala
index 10d872d..44b8d7d 100644
--- a/stage1/ClassLoaderCache.scala
+++ b/stage1/ClassLoaderCache.scala
@@ -15,5 +15,15 @@ class ClassLoaderCache(logger: Logger){
new ConcurrentHashMap[AnyRef,ClassLoader],
Some(logger)
)
- override def toString = s"""ClassLoaderCache("""+ persistent.keys.keySet.toVector.map(_.toString).sorted.map(" "++_).mkString("\n","\n","\n") +""")"""
+ override def toString = (
+ s"ClassLoaderCache("
+ ++
+ persistent.keys.keySet.toVector.map(_.toString.split(":").mkString("\n")).sorted.mkString("\n\n","\n\n","\n\n")
+ ++
+ "---------"
+ ++
+ transient.keys.keySet.toVector.map(_.toString.split(":").mkString("\n")).sorted.mkString("\n\n","\n\n^","\n\n")
+ ++
+ ")"
+ )
}
diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala
index 3456e1f..77b88a2 100644
--- a/stage1/Stage1.scala
+++ b/stage1/Stage1.scala
@@ -41,7 +41,8 @@ case class Stage2Args(
cwd: File,
args: Seq[String],
cbtHasChanged: Boolean,
- logger: Logger
+ logger: Logger,
+ classLoaderCache: ClassLoaderCache
)
object Stage1{
@@ -70,13 +71,17 @@ object Stage1{
)
logger.stage1(s"calling CbtDependency.classLoader")
- if(cbtHasChanged){
+ if(cbtHasChanged) {
NailgunLauncher.stage2classLoader = CbtDependency().classLoader(classLoaderCache)
+ }else{
+ classLoaderCache.transient.get( CbtDependency().classpath.string, NailgunLauncher.stage2classLoader )
}
logger.stage1(s"Run Stage2")
+ val cl = NailgunLauncher.stage2classLoader
val exitCode = (
- NailgunLauncher.stage2classLoader.loadClass(
+ cl
+ .loadClass(
if(args.admin) "cbt.AdminStage2" else "cbt.Stage2"
)
.getMethod( "run", classOf[Stage2Args] )
@@ -87,7 +92,8 @@ object Stage1{
args.args.drop(1).toVector,
// launcher changes cause entire nailgun restart, so no need for them here
cbtHasChanged = cbtHasChanged,
- logger
+ logger,
+ classLoaderCache
)
) match {
case code: ExitCode => code
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala
index da9f8dd..7a2f8db 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -31,7 +31,7 @@ object CatchTrappedExitCode{
}
}
-case class Context( cwd: File, args: Seq[String], logger: Logger, cbtHasChanged: Boolean, classLoaderCache: ClassLoaderCache )
+case class Context( projectDirectory: File, cwd: File, args: Seq[String], logger: Logger, cbtHasChanged: Boolean, classLoaderCache: ClassLoaderCache )
class BaseLib{
def realpath(name: File) = new File(Paths.get(name.getAbsolutePath).normalize.toString)
diff --git a/stage1/logger.scala b/stage1/logger.scala
index 91a2412..c21dc86 100644
--- a/stage1/logger.scala
+++ b/stage1/logger.scala
@@ -11,7 +11,7 @@ case class Logger(enabledLoggers: Set[String], start: Long) {
def this(enabledLoggers: Option[String], start: Long) = this( enabledLoggers.toVector.flatMap( _.split(",") ).toSet, start )
def log(name: String, msg: => String) = {
- val timeTaken = ((start - System.currentTimeMillis) / 1000).toString
+ val timeTaken = ((System.currentTimeMillis.toDouble - start) / 1000).toString
System.err.println( s"[${" "*(6-timeTaken.size)}$timeTaken][$name] $msg" )
}
diff --git a/stage1/resolver.scala b/stage1/resolver.scala
index e8bfc07..e4af8a9 100644
--- a/stage1/resolver.scala
+++ b/stage1/resolver.scala
@@ -60,10 +60,9 @@ abstract class Dependency{
The implementation of this method is untested and likely buggy
at this stage.
*/
- private object cacheExportClasspathConcurrently extends Cache[Future[ClassPath]]
private def exportClasspathConcurrently(
latest: Map[(String, String),ArtifactInfo]//, cache: BuildCache
- )( implicit ec: ExecutionContext ): Future[ClassPath] = cacheExportClasspathConcurrently{
+ )( implicit ec: ExecutionContext ): Future[ClassPath] = {
Future.sequence( // trigger compilation / download of all dependencies first
this.dependencies.map{
d =>
@@ -86,7 +85,7 @@ abstract class Dependency{
)
}
- private def actual(current: Dependency, latest: Map[(String,String),Dependency]) = current match {
+ protected def actual(current: Dependency, latest: Map[(String,String),Dependency]) = current match {
case d: ArtifactInfo => latest((d.groupId,d.artifactId))
case d => d
}
@@ -104,34 +103,28 @@ abstract class Dependency{
)
)
} else {
- val (cachable, nonCachable) = dependencies.partition(_.canBeCached)
- new URLClassLoader(
- ClassPath.flatten( nonCachable.map(actual(_,latest)).map(_.exportedClasspath) ),
- cache.persistent.get(
- ClassPath.flatten( cachable.map(actual(_,latest)).map(_.exportedClasspath) ).string,
- new MultiClassLoader(
- cachable.map( _.classLoaderRecursion(latest, cache) )
- )
+ cache.transient.get(
+ dependencyClasspath.string,
+ new MultiClassLoader(
+ dependencies.map( _.classLoaderRecursion(latest, cache) )
)
)
- new MultiClassLoader(
- dependencies.map( _.classLoaderRecursion(latest, cache) )
- )
}
}
protected def classLoaderRecursion( latest: Map[(String,String),Dependency], cache: ClassLoaderCache ): ClassLoader = {
- if( canBeCached ){
- val a = actual( this, latest )
- cache.persistent.get(
- a.classpath.string,
- new cbt.URLClassLoader( a.exportedClasspath, dependencyClassLoader(latest, cache) )
- )
- } else {
- new cbt.URLClassLoader( exportedClasspath, dependencyClassLoader(latest, cache) )
- }
+ val a = actual( this, latest )
+ (
+ if( canBeCached ){
+ cache.persistent
+ } else {
+ cache.transient
+ }
+ ).get(
+ a.classpath.string,
+ new cbt.URLClassLoader( a.exportedClasspath, dependencyClassLoader(latest, cache) )
+ )
}
- private object classLoaderCache extends Cache[ClassLoader]
- def classLoader( cache: ClassLoaderCache ): ClassLoader = classLoaderCache{
+ def classLoader( cache: ClassLoaderCache ): ClassLoader = {
if( concurrencyEnabled ){
// trigger concurrent building / downloading dependencies
exportClasspathConcurrently
@@ -145,6 +138,7 @@ abstract class Dependency{
cache
)
}
+ // FIXME: these probably need to update outdated as well
def classpath : ClassPath = exportedClasspath ++ dependencyClasspath
def dependencyJars : Seq[File] = transitiveDependencies.flatMap(_.jars)
def dependencyClasspath : ClassPath = ClassPath.flatten( transitiveDependencies.map(_.exportedClasspath) )
@@ -222,16 +216,6 @@ object Dependencies{
case class Stage1Dependency()(implicit val logger: Logger) extends Dependency{
override def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations
override def canBeCached = false
- /*
- private object classLoaderRecursionCache extends Cache[ClassLoader]
- override def classLoaderRecursion(latest: Map[(String,String),Dependency], cache: ClassLoaderCache) = classLoaderRecursionCache{
- println(System.currentTimeMillis)
- val cl = getClass.getClassLoader
- println(System.currentTimeMillis)
- cl
- ClassLoader.getSystemClassLoader
- }
- */
override def targetClasspath = exportedClasspath
override def exportedClasspath = ClassPath( Seq(nailgunTarget, stage1Target) )
override def exportedJars = ???//Seq[File]()
@@ -242,8 +226,13 @@ case class Stage1Dependency()(implicit val logger: Logger) extends Dependency{
)
)
// FIXME: implement sanity check to prevent using incompatible scala-library and xml version on cp
- override def classLoaderRecursion( latest: Map[(String,String),Dependency], cache: ClassLoaderCache )
- = getClass.getClassLoader
+ override def classLoaderRecursion( latest: Map[(String,String),Dependency], cache: ClassLoaderCache ) = {
+ val a = actual( this, latest )
+ cache.transient.get(
+ a.classpath.string,
+ getClass.getClassLoader
+ )
+ }
}
case class CbtDependency()(implicit val logger: Logger) extends Dependency{
override def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations
diff --git a/stage2/AdminStage2.scala b/stage2/AdminStage2.scala
index 883b5ed..9d7dcb2 100644
--- a/stage2/AdminStage2.scala
+++ b/stage2/AdminStage2.scala
@@ -4,7 +4,7 @@ object AdminStage2 extends Stage2Base{
def run( _args: Stage2Args ): Unit = {
val args = _args.args.dropWhile(Seq("admin","direct") contains _)
val lib = new Lib(_args.logger)
- val adminTasks = new AdminTasks(lib, args, _args.cwd)
+ val adminTasks = new AdminTasks(lib, args, _args.cwd, _args.classLoaderCache)
new lib.ReflectObject(adminTasks){
def usage: String = "Available methods: " ++ lib.taskNames(adminTasks.getClass).mkString(" ")
}.callNullary(args.lift(0))
diff --git a/stage2/AdminTasks.scala b/stage2/AdminTasks.scala
index 655b2b0..f189805 100644
--- a/stage2/AdminTasks.scala
+++ b/stage2/AdminTasks.scala
@@ -2,7 +2,7 @@ package cbt
import scala.collection.immutable.Seq
import java.io.{Console=>_,_}
import java.nio.file._
-class AdminTasks(lib: Lib, args: Seq[String], cwd: File){
+class AdminTasks(lib: Lib, args: Seq[String], cwd: File, classLoaderCache: ClassLoaderCache){
implicit val logger: Logger = lib.logger
def resolve = {
ClassPath.flatten(
@@ -31,14 +31,14 @@ class AdminTasks(lib: Lib, args: Seq[String], cwd: File){
)
// FIXME: this does not work quite yet, throws NoSuchFileException: /ammonite/repl/frontend/ReplBridge$.class
lib.runMain(
- "ammonite.repl.Main", Seq(), d.classLoader(new ClassLoaderCache(logger))
+ "ammonite.repl.Main", Seq(), d.classLoader(classLoaderCache)
)
}
def scala = {
val version = args.lift(1).getOrElse(constants.scalaVersion)
val scalac = new ScalaCompilerDependency( version )
lib.runMain(
- "scala.tools.nsc.MainGenericRunner", Seq("-cp", scalac.classpath.string), scalac.classLoader(new ClassLoaderCache(logger))
+ "scala.tools.nsc.MainGenericRunner", Seq("-cp", scalac.classpath.string), scalac.classLoader(classLoaderCache)
)
}
def scaffoldBasicBuild: Unit = lib.scaffoldBasicBuild( cwd )
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index 39cc9e3..2b49c93 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -23,7 +23,7 @@ class Build(val context: Context) extends Dependency with TriggerLoop with SbtDe
override def canBeCached = false
def enableConcurrency = false
- final def projectDirectory: File = lib.realpath(context.cwd)
+ final def projectDirectory: File = lib.realpath(context.projectDirectory)
assert( projectDirectory.exists, "projectDirectory does not exist: " ++ projectDirectory.string )
final def usage: String = lib.usage(this.getClass, context)
@@ -89,7 +89,7 @@ class Build(val context: Context) extends Dependency with TriggerLoop with SbtDe
) = lib.ScalaDependency( groupId, artifactId, version, classifier, scalaVersion )
final def BuildDependency(path: File) = cbt.BuildDependency(
- context.copy( cwd = path, args = Seq() )
+ context.copy( projectDirectory = path, args = Seq() )
)
def triggerLoopFiles: Seq[File] = sources ++ transitiveDependencies.collect{ case b: TriggerLoop => b.triggerLoopFiles }.flatten
@@ -133,6 +133,7 @@ class Build(val context: Context) extends Dependency with TriggerLoop with SbtDe
def test: ExitCode = lib.test(context)
+ /*
context.logger.composition(">"*80)
context.logger.composition("class " ++ this.getClass.toString)
context.logger.composition("dir " ++ projectDirectory.string)
@@ -141,6 +142,7 @@ class Build(val context: Context) extends Dependency with TriggerLoop with SbtDe
context.logger.composition("context " ++ context.toString)
context.logger.composition("dependencyTree\n" ++ dependencyTree)
context.logger.composition("<"*80)
+ */
// ========== cbt internals ==========
private[cbt] def finalBuild = this
diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala
index 813b44b..c52c3c6 100644
--- a/stage2/BuildBuild.scala
+++ b/stage2/BuildBuild.scala
@@ -6,22 +6,20 @@ class BuildBuild(context: Context) extends Build(context){
override def dependencies = Seq( CbtDependency()(context.logger) ) ++ super.dependencies
def managedBuildDirectory: File = lib.realpath( projectDirectory.parent )
val managedBuild = try{
- val managedContext = context.copy( cwd = managedBuildDirectory )
- val cl = new cbt.URLClassLoader(
- exportedClasspath,
- classOf[BuildBuild].getClassLoader // FIXME: this looks wrong. Should be ClassLoader.getSystemClassLoader but that crashes
- )
- cl
- .loadClass(lib.buildClassName)
- .getConstructor(classOf[Context])
- .newInstance(managedContext)
- .asInstanceOf[Build]
+ val managedContext = context.copy( projectDirectory = managedBuildDirectory )
+ val cl = classLoader(context.classLoaderCache)
+ logger.composition("Loading build at "+managedContext.projectDirectory)
+ cl
+ .loadClass(lib.buildClassName)
+ .getConstructor(classOf[Context])
+ .newInstance(managedContext)
+ .asInstanceOf[Build]
} catch {
case e: ClassNotFoundException if e.getMessage == lib.buildClassName =>
- throw new Exception("You need to remove the directory or define a class Build in: "+context.cwd)
+ throw new Exception("You need to remove the directory or define a class Build in: "+context.projectDirectory)
case e: Exception =>
- throw new Exception("during build: "+context.cwd, e)
+ throw new Exception("during build: "+context.projectDirectory, e)
}
override def triggerLoopFiles = super.triggerLoopFiles ++ managedBuild.triggerLoopFiles
- override def finalBuild = managedBuild.finalBuild
+ override def finalBuild = if( context.projectDirectory == context.cwd ) this else managedBuild.finalBuild
}
diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala
index 19357f9..8965cee 100644
--- a/stage2/BuildDependency.scala
+++ b/stage2/BuildDependency.scala
@@ -16,7 +16,7 @@ trait TriggerLoop extends Dependency{
}
/** You likely want to use the factory method in the BasicBuild class instead of this. */
case class BuildDependency(context: Context) extends TriggerLoop{
- override def show = this.getClass.getSimpleName ++ "(" ++ context.cwd.string ++ ")"
+ override def show = this.getClass.getSimpleName ++ "(" ++ context.projectDirectory.string ++ ")"
final override lazy val logger = context.logger
final override lazy val lib: Lib = new Lib(logger)
private val root = lib.loadRoot( context.copy(args=Seq()) )
@@ -31,7 +31,7 @@ case class BuildDependency(context: Context) extends TriggerLoop{
}
/*
case class DependencyOr(first: BuildDependency, second: JavaDependency) extends ProjectProxy with BuildDependencyBase{
- val isFirst = new File(first.context.cwd).exists
+ val isFirst = new File(first.context.projectDirectory).exists
def triggerLoopFiles = if(isFirst) first.triggerLoopFiles else Seq()
protected val delegate = if(isFirst) first else second
}
diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala
index 27bf253..16423df 100644
--- a/stage2/GitDependency.scala
+++ b/stage2/GitDependency.scala
@@ -38,7 +38,7 @@ case class GitDependency(
}
val managedBuild = lib.loadDynamic(
- context.copy( cwd = checkoutDirectory, args = Seq() )
+ context.copy( projectDirectory = checkoutDirectory, args = Seq() )
)
Seq( managedBuild )
}
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index 6b6263c..dbd6108 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -34,19 +34,19 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
This can either the Build itself, of if exists a BuildBuild or a BuildBuild for a BuildBuild and so on.
*/
def loadRoot(context: Context, default: Context => Build = new Build(_)): Build = {
- context.logger.composition( context.logger.showInvocation("Build.loadRoot",context) )
- def findStartDir(cwd: File): File = {
- val buildDir = realpath( cwd ++ "/build" )
- if(buildDir.exists) findStartDir(buildDir) else cwd
+ context.logger.composition( context.logger.showInvocation("Build.loadRoot",context.projectDirectory) )
+ def findStartDir(projectDirectory: File): File = {
+ val buildDir = realpath( projectDirectory ++ "/build" )
+ if(buildDir.exists) findStartDir(buildDir) else projectDirectory
}
- val start = findStartDir(context.cwd)
+ val start = findStartDir(context.projectDirectory)
- val useBasicBuildBuild = context.cwd == start
+ val useBasicBuildBuild = context.projectDirectory == start
val rootBuildClassName = if( useBasicBuildBuild ) buildBuildClassName else buildClassName
try{
- if(useBasicBuildBuild) default( context ) else new cbt.BuildBuild( context.copy( cwd = start ) )
+ if(useBasicBuildBuild) default( context ) else new cbt.BuildBuild( context.copy( projectDirectory = start ) )
} catch {
case e:ClassNotFoundException if e.getMessage == rootBuildClassName =>
throw new Exception(s"no class $rootBuildClassName found in " ++ start.string)
@@ -110,7 +110,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
logger.lib(s"invoke testDefault( $context )")
val exitCode: ExitCode = loadDynamic(
- context.copy( cwd = context.cwd ++ "/test", args = loggerArg.toVector ++ context.args ),
+ context.copy( projectDirectory = context.projectDirectory ++ "/test", args = loggerArg.toVector ++ context.args ),
new Build(_) with mixins.Test
).run
logger.lib(s"return testDefault( $context )")
@@ -145,7 +145,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
(
(
if( thisTasks.nonEmpty ){
- s"""Methods provided by Build ${context.cwd}
+ s"""Methods provided by Build ${context.projectDirectory}
${thisTasks.mkString(" ")}
diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala
index 4ae149c..ddadfb6 100644
--- a/stage2/Stage2.scala
+++ b/stage2/Stage2.scala
@@ -22,8 +22,8 @@ object Stage2 extends Stage2Base{
0
}
val task = args.args.lift( taskIndex )
-
- val context = Context( args.cwd, args.args.drop( taskIndex ), logger, args.cbtHasChanged, new ClassLoaderCache(logger) )
+
+ val context = Context( args.cwd, args.cwd, args.args.drop( taskIndex ), logger, args.cbtHasChanged, args.classLoaderCache )
val first = lib.loadRoot( context )
val build = first.finalBuild
@@ -44,9 +44,8 @@ object Stage2 extends Stage2Base{
case file if triggerFiles.exists(file.toString startsWith _.toString) =>
val build = lib.loadDynamic(context)
- val reflectBuild = new lib.ReflectBuild( build )
logger.loop(s"Re-running $task for " ++ build.projectDirectory.toString)
- reflectBuild.callNullary(task)
+ new lib.ReflectBuild(build).callNullary(task)
}
} else {
new lib.ReflectBuild(build).callNullary(task)
diff --git a/test/test.scala b/test/test.scala
index 7bd2c6a..29e6afa 100644
--- a/test/test.scala
+++ b/test/test.scala
@@ -72,7 +72,7 @@ object Main{
logger.test( "Running tests " ++ _args.toList.toString )
{
- val noContext = Context(cbtHome ++ "/test/nothing", Seq(), logger, false, new ClassLoaderCache(logger))
+ val noContext = Context(cbtHome ++ "/test/nothing", cbtHome, Seq(), logger, false, new ClassLoaderCache(logger))
val b = new Build(noContext){
override def dependencies = Seq(
MavenRepository.central.resolve(