From e8673866b79f7473391dcee26243eee80d5d3cb6 Mon Sep 17 00:00:00 2001 From: Christopher Vogt Date: Thu, 9 Feb 2017 21:20:11 -0500 Subject: idempotent change propagation using lastModified instead of a non-idempotent needsUpdate flag this fixes a bug where dependees would not be rebuilt if cbt exited or was killed after dependencies were already rebuilt. --- stage1/Stage1.scala | 100 ++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) (limited to 'stage1/Stage1.scala') diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala index 27c6402..1fd4663 100644 --- a/stage1/Stage1.scala +++ b/stage1/Stage1.scala @@ -39,7 +39,8 @@ abstract class Stage2Base{ case class Stage2Args( cwd: File, args: Seq[String], - cbtHasChanged: Boolean, + stage2LastModified: Long, + logger: Logger, classLoaderCache: ClassLoaderCache, cache: File, cbtHome: File, @@ -47,8 +48,9 @@ case class Stage2Args( )( implicit val transientCache: java.util.Map[AnyRef,AnyRef] ){ - val ClassLoaderCache( logger, persistentCache ) = classLoaderCache + val persistentCache = classLoaderCache.hashMap } + object Stage1{ protected def newerThan( a: File, b: File ) ={ a.lastModified > b.lastModified @@ -56,13 +58,14 @@ object Stage1{ def getBuild( _context: java.lang.Object, buildStage1: BuildStage1Result ) = { val context = _context.asInstanceOf[Context] - val logger = new Logger( context.enabledLoggers, context.start ) - val (changed, classLoader) = buildStage2( + val logger = new Logger( context.enabledLoggers, buildStage1.start ) + val (cbtLastModified, classLoader) = buildStage2( buildStage1, - ClassLoaderCache( logger, context.persistentCache ), + new ClassLoaderCache( context.persistentCache ), context.cbtHome, - context.cache - )( context.transientCache ) + context.cache, + logger + )(context.transientCache) classLoader .loadClass("cbt.Stage2") @@ -70,15 +73,16 @@ object Stage1{ .invoke( null, context.copy( - cbtHasChanged = context.cbtHasChanged || buildStage1.changed || changed // might be redundant + cbtLastModified = Math.max( context.cbtLastModified, cbtLastModified ) ) ) } def buildStage2( - buildStage1: BuildStage1Result, classLoaderCache: ClassLoaderCache, cbtHome: File, cache: File - )(implicit transientCache: java.util.Map[AnyRef,AnyRef]): (Boolean, ClassLoader) = { - import classLoaderCache.logger + buildStage1: BuildStage1Result, classLoaderCache: ClassLoaderCache, cbtHome: File, cache: File, logger: Logger + )(implicit transientCache: java.util.Map[AnyRef,AnyRef]): (Long, ClassLoader) = { + + import buildStage1._ val lib = new Stage1Lib(logger) import lib._ @@ -89,61 +93,58 @@ object Stage1{ stage2.listFiles ++ (stage2 ++ "/plugins").listFiles ).toVector.filter(_.isFile).filter(_.toString.endsWith(".scala")) - val cbtHasChanged = buildStage1.changed || lib.needsUpdate(stage2sourceFiles, stage2StatusFile) - val cls = this.getClass.getClassLoader.loadClass("cbt.NailgunLauncher") - val cbtDependency = new CbtDependency(cbtHasChanged, mavenCache, nailgunTarget, stage1Target, stage2Target, new File(buildStage1.compatibilityClasspath)) + def cbtDependencies = new CbtDependencies( + mavenCache, nailgunTarget, stage1Target, stage2Target, + new File(buildStage1.compatibilityClasspath) + ) logger.stage1("Compiling stage2 if necessary") - compile( - cbtHasChanged, - cbtHasChanged, + val Some( stage2LastModified ) = compile( + buildStage1.stage1LastModified, stage2sourceFiles, stage2Target, stage2StatusFile, - cbtDependency.dependencies, + cbtDependencies.stage2Dependency.dependencies, mavenCache, Seq("-deprecation","-feature","-unchecked"), classLoaderCache, zincVersion = constants.zincVersion, scalaVersion = constants.scalaVersion )(transientCache) logger.stage1(s"calling CbtDependency.classLoader") - if( cbtHasChanged && classLoaderCache.cache.containsKey( cbtDependency.classpath.string ) ) { - classLoaderCache.cache.remove( cbtDependency.classpath.string ) - } else { - assert( - buildStage1.compatibilityClasspath === cbtDependency.stage1Dependency.compatibilityDependency.classpath.string, - "compatibility classpath different from NailgunLauncher" - ) - assert( - buildStage1.stage1Classpath === cbtDependency.stage1Dependency.classpath.string, - "stage1 classpath different from NailgunLauncher" - ) - assert( - classLoaderCache.cache.containsKey( cbtDependency.stage1Dependency.compatibilityDependency.classpath.string ), - "cbt unchanged, expected compatibility classloader to be cached" - ) - assert( - classLoaderCache.cache.containsKey( cbtDependency.stage1Dependency.classpath.string ), - "cbt unchanged, expected stage1/nailgun classloader to be cached" - ) - } - val stage2ClassLoader = cbtDependency.classLoader(classLoaderCache) + assert( + buildStage1.compatibilityClasspath === cbtDependencies.compatibilityDependency.classpath.string, + "compatibility classpath different from NailgunLauncher" + ) + assert( + buildStage1.stage1Classpath === cbtDependencies.stage1Dependency.classpath.string, + "stage1 classpath different from NailgunLauncher" + ) + assert( + classLoaderCache.containsKey( cbtDependencies.compatibilityDependency.classpath.string, cbtDependencies.compatibilityDependency.lastModified ), + "cbt unchanged, expected compatibility classloader to be cached" + ) + assert( + classLoaderCache.containsKey( cbtDependencies.stage1Dependency.classpath.string, cbtDependencies.stage1Dependency.lastModified ), + "cbt unchanged, expected stage1 classloader to be cached" + ) + + val stage2ClassLoader = cbtDependencies.stage2Dependency.classLoader(classLoaderCache) { // a few classloader sanity checks val compatibilityClassLoader = - cbtDependency.stage1Dependency.compatibilityDependency.classLoader(classLoaderCache) + cbtDependencies.compatibilityDependency.classLoader(classLoaderCache) assert( classOf[BuildInterface].getClassLoader == compatibilityClassLoader, classOf[BuildInterface].getClassLoader.toString ++ "\n\nis not the same as\n\n" ++ compatibilityClassLoader.toString ) //------------- val stage1ClassLoader = - cbtDependency.stage1Dependency.classLoader(classLoaderCache) + cbtDependencies.stage1Dependency.classLoader(classLoaderCache) assert( - classOf[Stage1Dependency].getClassLoader == stage1ClassLoader, - classOf[Stage1Dependency].getClassLoader.toString ++ "\n\nis not the same as\n\n" ++ stage1ClassLoader.toString + classOf[Stage1ArgsParser].getClassLoader == stage1ClassLoader, + classOf[Stage1ArgsParser].getClassLoader.toString ++ "\n\nis not the same as\n\n" ++ stage1ClassLoader.toString ) //------------- assert( @@ -152,7 +153,7 @@ object Stage1{ ) } - ( cbtHasChanged, stage2ClassLoader ) + ( stage2LastModified, stage2ClassLoader ) } def run( @@ -160,24 +161,23 @@ object Stage1{ cache: File, cbtHome: File, buildStage1: BuildStage1Result, - start: java.lang.Long, persistentCache: java.util.Map[AnyRef,AnyRef] ): Int = { val args = Stage1ArgsParser(_args.toVector) - val logger = new Logger(args.enabledLoggers, start) + val logger = new Logger(args.enabledLoggers, buildStage1.start) implicit val transientCache: java.util.Map[AnyRef,AnyRef] = new java.util.HashMap logger.stage1(s"Stage1 start") - val classLoaderCache = ClassLoaderCache( logger, persistentCache ) - + val classLoaderCache = new ClassLoaderCache( persistentCache ) - val (cbtHasChanged, classLoader) = buildStage2( buildStage1, classLoaderCache, cbtHome, cache ) + val (stage2LastModified, classLoader) = buildStage2( buildStage1, classLoaderCache, cbtHome, cache, logger ) val stage2Args = Stage2Args( new File( args.args(0) ), args.args.drop(1).dropWhile(_ == "direct").toVector, // launcher changes cause entire nailgun restart, so no need for them here - cbtHasChanged = cbtHasChanged, + stage2LastModified = stage2LastModified, + logger = logger, classLoaderCache = classLoaderCache, cache, cbtHome, -- cgit v1.2.3