aboutsummaryrefslogtreecommitdiff
path: root/stage1
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-02-09 21:20:11 -0500
committerChristopher Vogt <oss.nsp@cvogt.org>2017-02-09 22:43:00 -0500
commite8673866b79f7473391dcee26243eee80d5d3cb6 (patch)
tree16146affeebdb58cd302a1f8527220c906818c96 /stage1
parentbee13ba7a4458482ce00a5c6bae4cd64328c4e5e (diff)
downloadcbt-e8673866b79f7473391dcee26243eee80d5d3cb6.tar.gz
cbt-e8673866b79f7473391dcee26243eee80d5d3cb6.tar.bz2
cbt-e8673866b79f7473391dcee26243eee80d5d3cb6.zip
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.
Diffstat (limited to 'stage1')
-rw-r--r--stage1/CbtPaths.scala2
-rw-r--r--stage1/ClassLoaderCache.scala24
-rw-r--r--stage1/ContextImplementation.scala40
-rw-r--r--stage1/MavenRepository.scala6
-rw-r--r--stage1/Stage1.scala100
-rw-r--r--stage1/Stage1Lib.scala81
-rw-r--r--stage1/cbt.scala23
-rw-r--r--stage1/resolver.scala102
8 files changed, 183 insertions, 195 deletions
diff --git a/stage1/CbtPaths.scala b/stage1/CbtPaths.scala
index 71c2ef1..c8f2279 100644
--- a/stage1/CbtPaths.scala
+++ b/stage1/CbtPaths.scala
@@ -9,7 +9,9 @@ case class CbtPaths(private val cbtHome: File, private val cache: File){
private val target = NailgunLauncher.TARGET.stripSuffix("/")
val stage1Target: File = stage1 ++ ("/" ++ target)
val stage2Target: File = stage2 ++ ("/" ++ target)
+ val stage1StatusFile: File = stage1Target ++ ".last-success"
val stage2StatusFile: File = stage2Target ++ ".last-success"
val compatibility: File = cbtHome ++ "/compatibility"
val nailgunTarget: File = nailgun ++ ("/" ++ target)
+ val nailgunStatusFile: File = nailgunTarget ++ ".last-success"
}
diff --git a/stage1/ClassLoaderCache.scala b/stage1/ClassLoaderCache.scala
deleted file mode 100644
index af0970e..0000000
--- a/stage1/ClassLoaderCache.scala
+++ /dev/null
@@ -1,24 +0,0 @@
-package cbt
-
-import java.net._
-import java.util._
-import collection.JavaConverters._
-
-case class ClassLoaderCache(
- logger: Logger,
- private[cbt] hashMap: java.util.Map[AnyRef,AnyRef]
-){
- val cache = new KeyLockedLazyCache[ClassLoader]( hashMap, Some(logger) )
- override def toString = (
- s"ClassLoaderCache("
- ++
- hashMap.asScala.collect{
- case (key, value) if key.isInstanceOf[String] =>
- key.toString.split(":").mkString("\n") -> value
- }.toVector.sortBy(_._1).map{
- case (key, value) => key + " -> " + hashMap.get(value)
- }.mkString("\n\n","\n\n","\n\n")
- ++
- ")"
- )
-}
diff --git a/stage1/ContextImplementation.scala b/stage1/ContextImplementation.scala
index 30db597..69094b0 100644
--- a/stage1/ContextImplementation.scala
+++ b/stage1/ContextImplementation.scala
@@ -1,20 +1,28 @@
package cbt
import java.io._
-import java.lang._
class ContextImplementation(
- val projectDirectory: File,
- val cwd: File,
- val argsArray: Array[String],
- val enabledLoggersArray: Array[String],
- val startCompat: Long,
- val cbtHasChangedCompat: Boolean,
- val scalaVersionOrNull: String,
- val persistentCache: java.util.Map[AnyRef,AnyRef],
- val transientCache: java.util.Map[AnyRef,AnyRef],
- val cache: File,
- val cbtHome: File,
- val cbtRootHome: File,
- val compatibilityTarget: File,
- val parentBuildOrNull: BuildInterface
-) extends Context
+ override val projectDirectory: File,
+ override val cwd: File,
+ override val argsArray: Array[String],
+ override val enabledLoggersArray: Array[String],
+ override val start: Long,
+ override val cbtLastModified: Long,
+ override val scalaVersionOrNull: String,
+ override val persistentCache: java.util.Map[AnyRef,AnyRef],
+ override val transientCache: java.util.Map[AnyRef,AnyRef],
+ override val cache: File,
+ override val cbtHome: File,
+ override val cbtRootHome: File,
+ override val compatibilityTarget: File,
+ override val parentBuildOrNull: BuildInterface
+) extends Context{
+ @deprecated("this method is replaced by cbtLastModified","")
+ def cbtHasChangedCompat = true
+ @deprecated("this method is replaced by start","")
+ def startCompat = start
+ @deprecated("this methods is replaced by persistentCache","")
+ def permanentKeys = throw new IncompatibleCbtVersionException("You need to upgrade your CBT version in this module. The Context field permanentClassLoaders is no longer supported.");
+ @deprecated("this methods is replaced by persistentCache","")
+ def permanentClassLoaders = throw new IncompatibleCbtVersionException("You need to upgrade your CBT version in this module. The Context field permanentClassLoaders is no longer supported.");
+}
diff --git a/stage1/MavenRepository.scala b/stage1/MavenRepository.scala
index 2ac7064..a8c9b51 100644
--- a/stage1/MavenRepository.scala
+++ b/stage1/MavenRepository.scala
@@ -2,12 +2,12 @@ package cbt
import java.io._
import java.net._
case class MavenResolver(
- cbtHasChanged: Boolean, mavenCache: File, urls: URL*
+ cbtLastModified: Long, mavenCache: File, urls: URL*
)(
implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]
){
def bind( dependencies: MavenDependency* ): Seq[BoundMavenDependency]
- = dependencies.map( BoundMavenDependency(cbtHasChanged,mavenCache,_,urls.to) ).to
+ = dependencies.map( BoundMavenDependency(cbtLastModified,mavenCache,_,urls.to) ).to
def bindOne( dependency: MavenDependency ): BoundMavenDependency
- = BoundMavenDependency( cbtHasChanged, mavenCache, dependency, urls.to )
+ = BoundMavenDependency( cbtLastModified, mavenCache, dependency, urls.to )
}
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,
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala
index f0bb588..ad4b2d0 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -32,7 +32,7 @@ class BaseLib{
def realpath(name: File) = new File(java.nio.file.Paths.get(name.getAbsolutePath).normalize.toString)
}
-class Stage1Lib( val logger: Logger ) extends BaseLib{
+class Stage1Lib( logger: Logger ) extends BaseLib{
lib =>
implicit val implicitLogger: Logger = logger
@@ -183,15 +183,10 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
}
}
- def needsUpdate( sourceFiles: Seq[File], statusFile: File ) = {
- val lastCompile = statusFile.lastModified
- sourceFiles.filter(_.lastModified > lastCompile).nonEmpty
- }
def compile(
- cbtHasChanged: Boolean,
- needsRecompile: Boolean,
- files: Seq[File],
+ cbtLastModified: Long,
+ sourceFiles: Seq[File],
compileTarget: File,
statusFile: File,
dependencies: Seq[Dependency],
@@ -202,20 +197,23 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
scalaVersion: String
)(
implicit transientCache: java.util.Map[AnyRef, AnyRef]
- ): Option[File] = {
- val classpath = Dependencies(dependencies).classpath
+ ): Option[Long] = {
+ val d = Dependencies(dependencies)
+ val classpath = d.classpath
val cp = classpath.string
if(classpath.files.isEmpty)
- throw new Exception("Trying to compile with empty classpath. Source files: " ++ files.toString)
+ throw new Exception("Trying to compile with empty classpath. Source files: " ++ sourceFiles.toString)
- if( files.isEmpty ){
+ if( sourceFiles.isEmpty ){
None
}else{
- if( needsRecompile ){
- def Resolver(urls: URL*) = MavenResolver(cbtHasChanged, mavenCache, urls: _*)
+ val start = System.currentTimeMillis
+ val lastCompiled = statusFile.lastModified
+ if( d.lastModified > lastCompiled || sourceFiles.exists(_.lastModified > lastCompiled) ){
+ def Resolver(urls: URL*) = MavenResolver(cbtLastModified, mavenCache, urls: _*)
val zinc = Resolver(mavenCentral).bindOne(MavenDependency("com.typesafe.zinc","zinc", zincVersion))
val zincDeps = zinc.transitiveDependencies
-
+
val sbtInterface =
zincDeps
.collect{ case d @
@@ -242,8 +240,6 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
val scalaReflect = Resolver(mavenCentral).bindOne(MavenDependency("org.scala-lang","scala-reflect",scalaVersion)).jar
val scalaCompiler = Resolver(mavenCentral).bindOne(MavenDependency("org.scala-lang","scala-compiler",scalaVersion)).jar
- val start = System.currentTimeMillis
-
val _class = "com.typesafe.zinc.Main"
val dualArgs =
Seq(
@@ -264,7 +260,7 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
_class,
dualArgs ++ singleArgs ++ Seq(
"-cp", cp // let's put cp last. It so long
- ) ++ files.map(_.toString),
+ ) ++ sourceFiles.map(_.toString),
zinc.classLoader(classLoaderCache)
)
} catch {
@@ -283,7 +279,7 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
-cp \\
${classpath.strings.mkString(":\\\n")} \\
\\
- ${files.sorted.mkString(" \\\n")}
+ ${sourceFiles.sorted.mkString(" \\\n")}
"""
)
@@ -300,8 +296,10 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
} else {
System.exit(code.integer) // FIXME: let's find a better solution for error handling. Maybe a monad after all.
}
+ Some( start )
+ } else {
+ Some( lastCompiled )
}
- Some( compileTarget )
}
}
def redirectOutToErr[T](code: => T): T = {
@@ -360,11 +358,11 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
)
def cacheOnDisk[T]
- ( cbtHasChanged: Boolean, cacheFile: File )
+ ( cbtLastModified: Long, cacheFile: File )
( deserialize: String => T )
( serialize: T => String )
( compute: => Seq[T] ) = {
- if(!cbtHasChanged && cacheFile.exists){
+ if(cacheFile.exists && cacheFile.lastModified > cbtLastModified ){
import collection.JavaConversions._
Files
.readAllLines( cacheFile.toPath, StandardCharsets.UTF_8 )
@@ -380,7 +378,7 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
def dependencyTreeRecursion(root: Dependency, indent: Int = 0): String = (
( " " * indent )
- ++ (if(root.needsUpdate) red(root.show) else root.show)
+ ++ root.show // (if(root.needsUpdate) red(root.show) else root.show)
++ root.dependencies.map( d =>
"\n" ++ dependencyTreeRecursion(d,indent + 1)
).mkString
@@ -420,33 +418,40 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
def classLoaderRecursion( dependency: Dependency, latest: Map[(String,String),Dependency], cache: ClassLoaderCache ): ClassLoader = {
// FIXME: shouldn't we be using KeyLockedLazyCache instead of hashmap directly here?
- val d = dependency
val dependencies = dependency.dependencies
- def dependencyClassLoader( latest: Map[(String,String),Dependency], cache: ClassLoaderCache ): ClassLoader = {
+ val dependencyClassLoader: ClassLoader = {
if( dependency.dependencies.isEmpty ){
// wrap for caching
new cbt.URLClassLoader( ClassPath(), ClassLoader.getSystemClassLoader().getParent() )
} else if( dependencies.size == 1 ){
classLoaderRecursion( dependencies.head, latest, cache )
} else{
- val cp = d.dependencyClasspath.string
- if( dependencies.exists(_.needsUpdate) && cache.cache.containsKey(cp) ){
- cache.cache.remove(cp)
- }
- def cl = new MultiClassLoader( dependencies.map( classLoaderRecursion(_, latest, cache) ) )
- if(d.isInstanceOf[BuildInterface])
+ val lastModified = dependencies.map( _.lastModified ).max
+ val cp = dependency.dependencyClasspath.string
+ val cl =
+ new MultiClassLoader(
+ dependencies.map( classLoaderRecursion(_, latest, cache) )
+ )
+ if(dependency.isInstanceOf[BuildInterface])
cl // Don't cache builds right now. We need to fix invalidation first.
- else
- cache.cache.get( cp, cl )
+ else{
+ if( !cache.containsKey( cp, lastModified ) ){
+ cache.put( cp, cl, lastModified )
+ }
+ cache.get( cp, lastModified )
+ }
}
}
val a = actual( dependency, latest )
- def cl = new cbt.URLClassLoader( a.exportedClasspath, dependencyClassLoader(latest, cache) )
- if(d.isInstanceOf[BuildInterface])
- cl
- else
- cache.cache.get( a.classpath.string, cl ).asInstanceOf[ClassLoader]
+ def cl = new cbt.URLClassLoader( a.exportedClasspath, dependencyClassLoader )
+
+ val cp = a.classpath.string
+ val lastModified = a.lastModified
+ if( !cache.containsKey( cp, lastModified ) ){
+ cache.put( cp, cl, lastModified )
+ }
+ cache.get( cp, lastModified )
}
}
diff --git a/stage1/cbt.scala b/stage1/cbt.scala
index 01c9303..0b0ccbf 100644
--- a/stage1/cbt.scala
+++ b/stage1/cbt.scala
@@ -33,6 +33,11 @@ object `package`{
def ++( s: String ): URL = new URL( url.toString ++ s )
def string = url.toString
}
+ implicit class SeqExtensions[T](seq: Seq[T]){
+ def maxOption(implicit ev: Ordering[T]): Option[T] = try{ Some(seq.max) } catch {
+ case e:java.lang.UnsupportedOperationException if e.getMessage === "empty.max" => None
+ }
+ }
implicit class BuildInterfaceExtensions(build: BuildInterface){
import build._
// TODO: if every build has a method triggers a callback if files change
@@ -52,30 +57,30 @@ object `package`{
def exportedClasspath: ClassPath = ClassPath(exportedClasspathArray.to)
def classpath = exportedClasspath ++ dependencyClasspath
def dependencies: Seq[Dependency] = dependenciesArray.to
- def needsUpdate: Boolean = needsUpdateCompat
}
implicit class ContextExtensions(subject: Context){
import subject._
val paths = CbtPaths(cbtHome, cache)
implicit def logger: Logger = new Logger(enabledLoggers, start)
- def classLoaderCache: ClassLoaderCache = new ClassLoaderCache( logger, persistentCache )
- def cbtDependency = {
+ def classLoaderCache: ClassLoaderCache = new ClassLoaderCache( persistentCache )
+ def cbtDependencies = {
import paths._
- new CbtDependency(cbtHasChanged, mavenCache, nailgunTarget, stage1Target, stage2Target, compatibilityTarget)(logger, transientCache)
+ new CbtDependencies(mavenCache, nailgunTarget, stage1Target, stage2Target, compatibilityTarget)(logger, transientCache)
}
+ val cbtDependency = cbtDependencies.stage2Dependency
+
def args: Seq[String] = argsArray.to
def enabledLoggers: Set[String] = enabledLoggersArray.to
def scalaVersion = Option(scalaVersionOrNull)
def parentBuild = Option(parentBuildOrNull)
- def start: scala.Long = startCompat
- def cbtHasChanged: scala.Boolean = cbtHasChangedCompat
+ def cbtLastModified: scala.Long = subject.cbtLastModified
def copy(
projectDirectory: File = projectDirectory,
args: Seq[String] = args,
//enabledLoggers: Set[String] = enabledLoggers,
- cbtHasChanged: Boolean = cbtHasChanged,
+ cbtLastModified: Long = cbtLastModified,
scalaVersion: Option[String] = scalaVersion,
cbtHome: File = cbtHome,
parentBuild: Option[BuildInterface] = None
@@ -84,8 +89,8 @@ object `package`{
cwd,
args.to,
enabledLoggers.to,
- startCompat,
- cbtHasChangedCompat,
+ start,
+ cbtLastModified,
scalaVersion.getOrElse(null),
persistentCache,
transientCache,
diff --git a/stage1/resolver.scala b/stage1/resolver.scala
index 8e46135..4a39d14 100644
--- a/stage1/resolver.scala
+++ b/stage1/resolver.scala
@@ -18,22 +18,14 @@ trait DependencyImplementation extends Dependency{
*/
protected lazy val taskCache = new PerClassCache(transientCache, moduleKey)
- /**
- CAREFUL: this is never allowed to return true for the same dependency more than
- once in a single cbt run. Otherwise we can end up with multiple classloaders
- for the same classes since classLoaderRecursion recreates the classLoader when it
- sees this flag and it can be called multiple times. Maybe we can find a safer
- solution than this current state.
- */
- def needsUpdate: Boolean
- //def cacheClassLoader: Boolean = false
private[cbt] def targetClasspath: ClassPath
def dependencyClasspathArray: Array[File] = dependencyClasspath.files.toArray
def exportedClasspathArray: Array[File] = exportedClasspath.files.toArray
def exportedClasspath: ClassPath
def dependenciesArray: Array[Dependency] = dependencies.to
- def needsUpdateCompat: java.lang.Boolean = needsUpdate
+ @deprecated("this method is replaced by lastModifiedCompat","")
+ def needsUpdateCompat = true
/*
//private type BuildCache = KeyLockedLazyCache[Dependency, Future[ClassPath]]
@@ -122,63 +114,63 @@ trait DependencyImplementation extends Dependency{
}
// TODO: all this hard codes the scala version, needs more flexibility
-class ScalaCompilerDependency(cbtHasChanged: Boolean, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BoundMavenDependency(cbtHasChanged, mavenCache, MavenDependency("org.scala-lang","scala-compiler",version, Classifier.none), Seq(mavenCentral))
-class ScalaLibraryDependency (cbtHasChanged: Boolean, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BoundMavenDependency(cbtHasChanged, mavenCache, MavenDependency("org.scala-lang","scala-library",version, Classifier.none), Seq(mavenCentral))
-class ScalaReflectDependency (cbtHasChanged: Boolean, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BoundMavenDependency(cbtHasChanged, mavenCache, MavenDependency("org.scala-lang","scala-reflect",version, Classifier.none), Seq(mavenCentral))
+class ScalaCompilerDependency(cbtLastModified: Long, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BoundMavenDependency(cbtLastModified, mavenCache, MavenDependency("org.scala-lang","scala-compiler",version, Classifier.none), Seq(mavenCentral))
+class ScalaLibraryDependency (cbtLastModified: Long, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BoundMavenDependency(cbtLastModified, mavenCache, MavenDependency("org.scala-lang","scala-library",version, Classifier.none), Seq(mavenCentral))
+class ScalaReflectDependency (cbtLastModified: Long, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BoundMavenDependency(cbtLastModified, mavenCache, MavenDependency("org.scala-lang","scala-reflect",version, Classifier.none), Seq(mavenCentral))
-class ScalaDependencies(cbtHasChanged: Boolean, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends Dependencies(
+class ScalaDependencies(cbtLastModified: Long, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends Dependencies(
Seq(
- new ScalaCompilerDependency(cbtHasChanged, mavenCache, version),
- new ScalaLibraryDependency(cbtHasChanged, mavenCache, version),
- new ScalaReflectDependency(cbtHasChanged, mavenCache, version)
+ new ScalaCompilerDependency(cbtLastModified, mavenCache, version),
+ new ScalaLibraryDependency(cbtLastModified, mavenCache, version),
+ new ScalaReflectDependency(cbtLastModified, mavenCache, version)
)
)
case class BinaryDependency( paths: Seq[File], dependencies: Seq[Dependency] )(implicit val logger: Logger, val transientCache: java.util.Map[AnyRef,AnyRef]) extends DependencyImplementation{
assert(paths.nonEmpty)
def exportedClasspath = ClassPath(paths)
- override def needsUpdate = false
+ override def lastModified = paths.map(_.lastModified).maxOption.getOrElse(0) // FIXME: cache this
def targetClasspath = exportedClasspath
def moduleKey = this.getClass.getName ++ "(" ++ paths.mkString(", ") ++ ")"
}
/** Allows to easily assemble a bunch of dependencies */
case class Dependencies( dependencies: Seq[Dependency] )(implicit val logger: Logger, val transientCache: java.util.Map[AnyRef,AnyRef]) extends DependencyImplementation{
- override def needsUpdate = dependencies.exists(_.needsUpdate)
- override def exportedClasspath = ClassPath()
- override def targetClasspath = ClassPath()
+ override def lastModified = dependencies.map(_.lastModified).maxOption.getOrElse(0)
def moduleKey = this.getClass.getName ++ "(" ++ dependencies.map(_.moduleKey).mkString(", ") ++ ")"
+ def targetClasspath = ClassPath()
+ def exportedClasspath = ClassPath()
}
-class Stage1Dependency(cbtHasChanged: Boolean, mavenCache: File, nailgunTarget: File, stage1Target: File, compatibilityTarget: File)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BinaryDependency(
- Seq(nailgunTarget, stage1Target),
- Seq(
- new CompatibilityDependency(cbtHasChanged, compatibilityTarget)
- ) ++
- MavenResolver(cbtHasChanged,mavenCache,mavenCentral).bind(
- MavenDependency("org.scala-lang","scala-library",constants.scalaVersion),
- MavenDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,constants.scalaXmlVersion)
- )
-){
- val compatibilityDependency = new CompatibilityDependency(cbtHasChanged, compatibilityTarget)
-
+case class PostBuildDependency(target: File, _dependencies: Seq[DependencyImplementation])(implicit val logger: Logger, val transientCache: java.util.Map[AnyRef,AnyRef]) extends DependencyImplementation{
+ override final lazy val lastModified = (target++".last-success").lastModified
+ def moduleKey = target.string
+ override def targetClasspath = exportedClasspath
+ override def exportedClasspath = ClassPath( Seq(target) )
+ override def dependencies = _dependencies
}
-
-class CompatibilityDependency(cbtHasChanged: Boolean, compatibilityTarget: File)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BinaryDependency(
- Seq(compatibilityTarget), Nil
-)
-
-class CbtDependency(cbtHasChanged: Boolean, mavenCache: File, nailgunTarget: File, stage1Target: File, stage2Target: File, compatibilityTarget: File)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends BinaryDependency(
- Seq( stage2Target ),
- Seq(
- new Stage1Dependency(cbtHasChanged, mavenCache, nailgunTarget, stage1Target, compatibilityTarget)
- ) ++
- MavenResolver(cbtHasChanged, mavenCache,mavenCentral).bind(
- MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0"),
- MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r")
+case class CbtDependencies(mavenCache: File, nailgunTarget: File, stage1Target: File, stage2Target: File, compatibilityTarget: File)(implicit logger: Logger, val transientCache: java.util.Map[AnyRef,AnyRef]){
+ val compatibilityDependency = PostBuildDependency(compatibilityTarget, Nil)
+ val cbtLastModified = (stage2Target++".last-success").lastModified
+ val stage1Dependency = PostBuildDependency(
+ stage1Target,
+ Seq(
+ PostBuildDependency(nailgunTarget, Nil),
+ compatibilityDependency
+ ) ++
+ MavenResolver(cbtLastModified,mavenCache,mavenCentral).bind(
+ MavenDependency("org.scala-lang","scala-library",constants.scalaVersion),
+ MavenDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,constants.scalaXmlVersion)
+ )
+ )
+ val stage2Dependency = PostBuildDependency(
+ stage2Target,
+ stage1Dependency +:
+ MavenResolver(cbtLastModified, mavenCache,mavenCentral).bind(
+ MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0"),
+ MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r")
+ )
)
-){
- val stage1Dependency = new Stage1Dependency(cbtHasChanged, mavenCache, nailgunTarget, stage1Target, compatibilityTarget)
}
case class Classifier(name: Option[String])
@@ -191,11 +183,11 @@ abstract class DependenciesProxy{
}
class BoundMavenDependencies(
- cbtHasChanged: Boolean, mavenCache: File, urls: Seq[URL], mavenDependencies: Seq[MavenDependency]
+ cbtLastModified: Long, mavenCache: File, urls: Seq[URL], mavenDependencies: Seq[MavenDependency]
)(
implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]
) extends Dependencies(
- mavenDependencies.map( BoundMavenDependency(cbtHasChanged,mavenCache,_,urls) )
+ mavenDependencies.map( BoundMavenDependency(cbtLastModified,mavenCache,_,urls) )
)
case class MavenDependency(
groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none
@@ -209,7 +201,7 @@ object MavenDependency{
}
// FIXME: take MavenResolver instead of mavenCache and repositories separately
case class BoundMavenDependency(
- cbtHasChanged: Boolean, mavenCache: File, mavenDependency: MavenDependency, repositories: Seq[URL]
+ cbtLastModified: Long, mavenCache: File, mavenDependency: MavenDependency, repositories: Seq[URL]
)(
implicit val logger: Logger, val transientCache: java.util.Map[AnyRef,AnyRef]
) extends ArtifactInfo with DependencyImplementation{
@@ -233,7 +225,7 @@ case class BoundMavenDependency(
)
override def show: String = this.getClass.getSimpleName ++ "(" ++ mavenDependency.serialize ++ ")"
- override def needsUpdate = false
+ override final lazy val lastModified = classpath.strings.map(new File(_).lastModified).max
private val groupPath = groupId.split("\\.").mkString("/")
protected[cbt] def basePath(useClassifier: Boolean) = s"/$groupPath/$artifactId/$version/$artifactId-$version" ++ (if (useClassifier) classifier.name.map("-"++_).getOrElse("") else "")
@@ -276,7 +268,7 @@ case class BoundMavenDependency(
(pomXml \ "parent").collect{
case parent =>
BoundMavenDependency(
- cbtHasChanged: Boolean,
+ cbtLastModified: Long,
mavenCache,
MavenDependency(
(parent \ "groupId").text,
@@ -314,7 +306,7 @@ case class BoundMavenDependency(
if(classifier == Classifier.sources) Seq()
else {
lib.cacheOnDisk(
- cbtHasChanged, mavenCache ++ basePath(true) ++ ".pom.dependencies"
+ cbtLastModified, mavenCache ++ basePath(true) ++ ".pom.dependencies"
)( MavenDependency.deserialize )( _.serialize ){
(pomXml \ "dependencies" \ "dependency").collect{
case xml if ( (xml \ "scope").text == "" || (xml \ "scope").text == "compile" ) && (xml \ "optional").text != "true" =>
@@ -339,7 +331,7 @@ case class BoundMavenDependency(
MavenDependency( groupId, artifactId, version, classifier )
}.toVector
}.map(
- BoundMavenDependency( cbtHasChanged, mavenCache, _, repositories )
+ BoundMavenDependency( cbtLastModified, mavenCache, _, repositories )
).to
}
}