diff options
-rw-r--r-- | stage1/Stage1.scala | 24 | ||||
-rw-r--r-- | stage1/Stage1Lib.scala | 39 | ||||
-rw-r--r-- | stage1/paths.scala | 1 | ||||
-rw-r--r-- | stage1/resolver.scala | 16 | ||||
-rw-r--r-- | stage2/BasicBuild.scala | 37 | ||||
-rw-r--r-- | stage2/BuildDependency.scala | 2 | ||||
-rw-r--r-- | stage2/GitDependency.scala | 2 | ||||
-rw-r--r-- | stage2/Lib.scala | 26 |
8 files changed, 70 insertions, 77 deletions
diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala index 32da3ed..be933d2 100644 --- a/stage1/Stage1.scala +++ b/stage1/Stage1.scala @@ -68,17 +68,21 @@ object Stage1{ val classLoaderCache = new ClassLoaderCache(logger) - val stage2SourcesChanged = sourceFiles.exists(newerThan(_, changeIndicator)) - logger.stage1("before conditionally running zinc to recompile CBT") - if( stage2SourcesChanged ) { - val scalaXml = JavaDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,constants.scalaXmlVersion) - logger.stage1("cbt.lib has changed. Recompiling.") - zinc( true, sourceFiles, stage2Target, nailgunTarget +: stage1Target +: Dependencies(deps, scalaXml).classpath, classLoaderCache, Seq("-deprecation") )( zincVersion = "0.3.9", scalaVersion = constants.scalaVersion ) - } + val stage2SourcesChanged = lib.needsUpdate(sourceFiles, stage2StatusFile) + logger.stage1("Compiling stage2 if necessary") + val scalaXml = JavaDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,constants.scalaXmlVersion) + compile( + stage2SourcesChanged, + sourceFiles, stage2Target, stage2StatusFile, + nailgunTarget +: stage1Target +: Dependencies(deps, scalaXml).classpath, + Seq("-deprecation"), classLoaderCache, + zincVersion = "0.3.9", scalaVersion = constants.scalaVersion + ) + logger.stage1(s"[$now] calling CbtDependency.classLoader") - val cl = classLoaderCache.transient.get( - (stage2Target +: deps.classpath).string, + val cl = /*classLoaderCache.transient.get( + (stage2Target +: deps.classpath).string,*/ cbt.URLClassLoader( ClassPath(Seq(stage2Target)), classLoaderCache.persistent.get( @@ -86,7 +90,7 @@ object Stage1{ cbt.URLClassLoader( deps.classpath, classLoader ) ) ) - ) + //) logger.stage1(s"[$now] Run Stage2") val exitCode = ( diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala index 69b828b..e4fe15e 100644 --- a/stage1/Stage1Lib.scala +++ b/stage1/Stage1Lib.scala @@ -6,6 +6,7 @@ import java.io._ import java.lang.reflect.InvocationTargetException import java.net._ import java.nio.file._ +import java.nio.file.attribute.FileTime import javax.tools._ import java.security._ import java.util._ @@ -111,14 +112,22 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ } } - def zinc( + def needsUpdate( sourceFiles: Seq[File], statusFile: File ) = { + val lastCompile = statusFile.lastModified + sourceFiles.filter(_.lastModified > lastCompile).nonEmpty + } + + def compile( needsRecompile: Boolean, files: Seq[File], compileTarget: File, + statusFile: File, classpath: ClassPath, + scalacOptions: Seq[String] = Seq(), classLoaderCache: ClassLoaderCache, - extraArgs: Seq[String] = Seq() - )( zincVersion: String, scalaVersion: String ): Unit = { + zincVersion: String, + scalaVersion: String + ): File = { val cp = classpath.string if(classpath.files.isEmpty) @@ -126,9 +135,6 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ if(files.isEmpty) throw new Exception("Trying to compile no files. ClassPath: " ++ cp) - - // only run zinc if files changed, for performance reasons - // FIXME: this is broken, need invalidate on changes in dependencies as well if( needsRecompile ){ val zinc = JavaDependency("com.typesafe.zinc","zinc", zincVersion) val zincDeps = zinc.transitiveDependencies @@ -151,6 +157,8 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ val scalaReflect = JavaDependency("org.scala-lang","scala-reflect",scalaVersion).jar val scalaCompiler = JavaDependency("org.scala-lang","scala-compiler",scalaVersion).jar + val start = System.currentTimeMillis + val code = redirectOutToErr{ lib.runMain( "com.typesafe.zinc.Main", @@ -162,22 +170,21 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ "-scala-extra", scalaReflect.toString, "-cp", cp, "-d", compileTarget.toString - ) ++ extraArgs.map("-S"++_) ++ files.map(_.toString), + ) ++ scalacOptions.map("-S"++_) ++ files.map(_.toString), zinc.classLoader(classLoaderCache) ) } - if(code != ExitCode.Success){ - // Ensure we trigger recompilation next time. This is currently required because we - // don't record the time of the last successful build elsewhere. But hopefully that will - // change soon. - val now = System.currentTimeMillis() - files.foreach(_.setLastModified(now)) - - // Tell the caller that things went wrong. - System.exit(code.integer) + if(code == ExitCode.Success){ + // write version and when last compilation started so we can trigger + // recompile if cbt version changed or newer source files are seen + Files.write(statusFile.toPath, "".getBytes)//cbtVersion.getBytes) + Files.setLastModifiedTime(statusFile.toPath, FileTime.fromMillis(start) ) + } else { + System.exit(code.integer) // FIXME: let's find a better solution for error handling. Maybe a monad after all. } } + compileTarget } def redirectOutToErr[T](code: => T): T = { val oldOut = System.out diff --git a/stage1/paths.scala b/stage1/paths.scala index e8e3cc5..f27e538 100644 --- a/stage1/paths.scala +++ b/stage1/paths.scala @@ -11,6 +11,7 @@ object paths{ private val target = Option(System.getenv("TARGET")).get.stripSuffix("/") val stage1Target: File = stage1 ++ ("/" ++ target) val stage2Target: File = stage2 ++ ("/" ++ target) + val stage2StatusFile: File = stage2Target ++ ".last-success" val nailgunTarget: File = nailgun ++ ("/" ++ target) val sonatypeLogin: File = cbtHome ++ "/sonatype.login" } diff --git a/stage1/resolver.scala b/stage1/resolver.scala index b8e6544..1dd4d6c 100644 --- a/stage1/resolver.scala +++ b/stage1/resolver.scala @@ -28,7 +28,7 @@ abstract class Dependency{ implicit def logger: Logger protected def lib = new Stage1Lib(logger) - def updated: Boolean + def needsUpdate: Boolean //def cacheClassLoader: Boolean = false private[cbt] def targetClasspath: ClassPath def exportedClasspath: ClassPath @@ -174,7 +174,7 @@ abstract class Dependency{ def dependencyTree: String = dependencyTreeRecursion() private def dependencyTreeRecursion(indent: Int = 0): String = ( ( " " * indent ) - ++ (if(updated) lib.red(show) else show) + ++ (if(needsUpdate) lib.red(show) else show) ++ dependencies.map( "\n" ++ _.dependencyTreeRecursion(indent + 1) ).mkString @@ -187,7 +187,7 @@ class ScalaLibraryDependency (version: String)(implicit logger: Logger) extends class ScalaReflectDependency (version: String)(implicit logger: Logger) extends JavaDependency("org.scala-lang","scala-reflect",version) case class ScalaDependencies(version: String)(implicit val logger: Logger) extends Dependency{ sd => - final val updated = false + override final val needsUpdate = false override def canBeCached = true def targetClasspath = ClassPath(Seq()) def exportedClasspath = ClassPath(Seq()) @@ -200,23 +200,23 @@ case class ScalaDependencies(version: String)(implicit val logger: Logger) exten } case class BinaryDependency( path: File, dependencies: Seq[Dependency] )(implicit val logger: Logger) extends Dependency{ - def updated = false def exportedClasspath = ClassPath(Seq(path)) def exportedJars = Seq[File](path) + override def needsUpdate = false def targetClasspath = exportedClasspath } /** Allows to easily assemble a bunch of dependencies */ case class Dependencies( _dependencies: Dependency* )(implicit val logger: Logger) extends Dependency{ override def dependencies = _dependencies.to - def updated = dependencies.exists(_.updated) + def needsUpdate = dependencies.exists(_.needsUpdate) def exportedClasspath = ClassPath(Seq()) def exportedJars = Seq() def targetClasspath = ClassPath(Seq()) } case class Stage1Dependency()(implicit val logger: Logger) extends Dependency{ - def updated = false // FIXME: think this through, might allow simplifications and/or optimizations + def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations override def canBeCached = false /* private object classLoaderRecursionCache extends Cache[ClassLoader] @@ -245,7 +245,7 @@ case class CbtDependency()(implicit val logger: Logger) extends Dependency{ "org.scala-lang.modules","scala-xml","1.0.5", scalaVersion = constants.scalaMajorVersion ) ) - def updated = false // FIXME: think this through, might allow simplifications and/or optimizations + def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations def targetClasspath = exportedClasspath } @@ -260,7 +260,7 @@ case class JavaDependency( groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none )(implicit val logger: Logger) extends ArtifactInfo{ - def updated = false + override def needsUpdate = false override def canBeCached = true private val groupPath = groupId.split("\\.").mkString("/") diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala index 28360aa..27de538 100644 --- a/stage2/BasicBuild.scala +++ b/stage2/BasicBuild.scala @@ -48,6 +48,12 @@ class Build(val context: Context) extends Dependency with TriggerLoop{ def apiTarget: File = scalaTarget ++ "/api" /** directory where the class files should be put (in package directories) */ def compileTarget: File = scalaTarget ++ "/classes" + /** + File which cbt uses to determine if it needs to trigger an incremental re-compile. + Last modified date is the time when the last successful compilation started. + Contents is the cbt version git hash. + */ + def compileStatusFile: File = compileTarget ++ ".last-success" /** Source directories and files. Defaults to .scala and .java files in src/ and top-level. */ def sources: Seq[File] = Seq(defaultSourceDirectory) ++ projectDirectory.listFiles.toVector.filter(sourceFileFilter) @@ -118,33 +124,20 @@ class Build(val context: Context) extends Dependency with TriggerLoop{ /** scalac options used for zinc and scaladoc */ def scalacOptions: Seq[String] = Seq( "-feature", "-deprecation", "-unchecked" ) - val updated: Boolean = { - val existingClassFiles = lib.listFilesRecursive(compileTarget) - val sourcesChanged = existingClassFiles.nonEmpty && { - val oldestClassFile = existingClassFiles.sortBy(_.lastModified).head - val oldestClassFileAge = oldestClassFile.lastModified - val changedSourceFiles = sourceFiles.filter(_.lastModified > oldestClassFileAge) - if(changedSourceFiles.nonEmpty){ - /* - println(changedSourceFiles) - println(changedSourceFiles.map(_.lastModified)) - println(changedSourceFiles.map(_.lastModified > oldestClassFileAge)) - println(oldestClassFile) - println(oldestClassFileAge) - println("-"*80) - */ - } - changedSourceFiles.nonEmpty - } - sourcesChanged || transitiveDependencies.map(_.updated).fold(false)(_ || _) + private object needsUpdateCache extends Cache[Boolean] + def needsUpdate: Boolean = { + needsUpdateCache( + lib.needsUpdate( sourceFiles, compileStatusFile ) + || transitiveDependencies.exists(_.needsUpdate) + ) } private object compileCache extends Cache[File] def compile: File = compileCache{ lib.compile( - updated, - sourceFiles, compileTarget, dependencyClasspath, scalacOptions, - zincVersion = zincVersion, scalaVersion = scalaVersion, context.classLoaderCache + needsUpdate, + sourceFiles, compileTarget, compileStatusFile, dependencyClasspath, scalacOptions, + context.classLoaderCache, zincVersion = zincVersion, scalaVersion = scalaVersion ) } diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala index 84a0100..e3a01c7 100644 --- a/stage2/BuildDependency.scala +++ b/stage2/BuildDependency.scala @@ -25,7 +25,7 @@ case class BuildDependency(context: Context) extends TriggerLoop{ def exportedJars = Seq() def dependencies = Seq(build) def triggerLoopFiles = root.triggerLoopFiles - final val updated = build.updated + override final val needsUpdate = build.needsUpdate def targetClasspath = ClassPath(Seq()) } /* diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala index 993825e..59de98a 100644 --- a/stage2/GitDependency.scala +++ b/stage2/GitDependency.scala @@ -45,5 +45,5 @@ case class GitDependency( def exportedClasspath = ClassPath(Seq()) def exportedJars = Seq() private[cbt] def targetClasspath = exportedClasspath - def updated: Boolean = false + def needsUpdate: Boolean = false } diff --git a/stage2/Lib.scala b/stage2/Lib.scala index d917e8b..fbbe870 100644 --- a/stage2/Lib.scala +++ b/stage2/Lib.scala @@ -53,18 +53,6 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{ } } - def compile( - updated: Boolean, - sourceFiles: Seq[File], compileTarget: File, dependenyClasspath: ClassPath, - compileArgs: Seq[String], zincVersion: String, scalaVersion: String, classLoaderCache: ClassLoaderCache - ): File = { - if(sourceFiles.nonEmpty) - lib.zinc( - updated, sourceFiles, compileTarget, dependenyClasspath, classLoaderCache, compileArgs - )( zincVersion = zincVersion, scalaVersion = scalaVersion ) - compileTarget - } - def srcJar(sources: Seq[File], artifactId: String, version: String, jarTarget: File): File = { val file = jarTarget ++ ("/"++artifactId++"-"++version++"-sources.jar") lib.jarFile(file, sources) @@ -96,14 +84,14 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{ "-d", apiTarget.toString ) ++ compileArgs ++ sourceFiles.map(_.toString) logger.lib("creating docs for source files "+args.mkString(", ")) - redirectOutToErr{ - runMain( - "scala.tools.nsc.ScalaDoc", - args, - ScalaDependencies(scalaVersion)(logger).classLoader(classLoaderCache) - ) - } + redirectOutToErr{ + runMain( + "scala.tools.nsc.ScalaDoc", + args, + ScalaDependencies(scalaVersion)(logger).classLoader(classLoaderCache) + ) } + } val docJar = jarTarget ++ ("/"++artifactId++"-"++version++"-javadoc.jar") lib.jarFile(docJar, Vector(apiTarget)) docJar |