aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Christopher Vogt <oss.nsp@cvogt.org>2017-02-10 01:11:22 -0500
committerGitHub <noreply@github.com>2017-02-10 01:11:22 -0500
commitd743136c6b98fa91f501cc15dc47530f8f93b8fe (patch)
treeaedb9af30e41f056aea148cc9e3b37d17327ca54
parente2cb8726735ba306ade3befefae3b87549b52c9f (diff)
parentf5e653e033e468c58e8a832e0dce3a7020a66ce4 (diff)
downloadcbt-d743136c6b98fa91f501cc15dc47530f8f93b8fe.tar.gz
cbt-d743136c6b98fa91f501cc15dc47530f8f93b8fe.tar.bz2
cbt-d743136c6b98fa91f501cc15dc47530f8f93b8fe.zip
Merge pull request #314 from cvogt/fix-update-bugs
better caching and change propagation fixing link-time errors
-rw-r--r--README.md2
-rwxr-xr-xcbt7
-rw-r--r--circle.yml8
-rw-r--r--compatibility/Context.java35
-rw-r--r--compatibility/Dependency.java15
-rw-r--r--compatibility/IncompatibleCbtVersionException.java10
-rw-r--r--compatibility/Result.java2
-rw-r--r--examples/scalajs-react-example/js/build/build.scala2
-rw-r--r--libraries/eval/build/build.scala2
-rw-r--r--nailgun_launcher/BuildStage1Result.java8
-rw-r--r--nailgun_launcher/CbtURLClassLoader.java33
-rw-r--r--nailgun_launcher/ClassLoaderCache.java66
-rw-r--r--nailgun_launcher/ClassLoaderCache2.java37
-rw-r--r--nailgun_launcher/EarlyDependencies.java210
-rw-r--r--nailgun_launcher/NailgunLauncher.java128
-rw-r--r--nailgun_launcher/Stage0Lib.java67
-rw-r--r--nailgun_launcher/TrapSecurityManager.java2
-rw-r--r--plugins/essentials/DynamicOverrides.scala10
-rw-r--r--plugins/scalajs/ScalaJsBuild.scala2
-rw-r--r--plugins/scalajs/ScalaJsLib.scala6
-rw-r--r--plugins/scalatest/ScalaTest.scala2
-rw-r--r--plugins/wartremover/WartRemover.scala2
-rw-r--r--stage1/Cache.scala14
-rw-r--r--stage1/CachingClassLoader.scala2
-rw-r--r--stage1/CbtPaths.scala2
-rw-r--r--stage1/ClassLoaderCache.scala24
-rw-r--r--stage1/ContextImplementation.scala44
-rw-r--r--stage1/KeyLockedLazyCache.scala61
-rw-r--r--stage1/MavenRepository.scala14
-rw-r--r--stage1/PoorMansProfiler.scala4
-rw-r--r--stage1/Stage1.scala126
-rw-r--r--stage1/Stage1Lib.scala118
-rw-r--r--stage1/cbt.scala36
-rw-r--r--stage1/logger.scala2
-rw-r--r--stage1/resolver.scala165
-rw-r--r--stage2/BasicBuild.scala55
-rw-r--r--stage2/BuildBuild.scala3
-rw-r--r--stage2/BuildDependency.scala5
-rw-r--r--stage2/GitDependency.scala12
-rw-r--r--stage2/Lib.scala8
-rw-r--r--stage2/PackageJars.scala13
-rw-r--r--stage2/Stage2.scala19
-rw-r--r--stage2/ToolsStage2.scala5
-rw-r--r--stage2/ToolsTasks.scala29
-rw-r--r--stage2/plugins/Dotty.scala47
-rw-r--r--test/library-test/build/build.scala2
-rw-r--r--test/simple-fixed-cbt/build/build.scala2
-rw-r--r--test/simple-fixed/build/build.scala2
-rw-r--r--test/simple/build/build.scala2
-rw-r--r--test/test.scala16
50 files changed, 811 insertions, 677 deletions
diff --git a/README.md b/README.md
index c01e8db..c353503 100644
--- a/README.md
+++ b/README.md
@@ -323,7 +323,7 @@ When you specify a particular version, CBT will use that one instead of the inst
You can specify one by adding one line right before `class Build`. It looks like this:
```
-// cbt:https://github.com/cvogt/cbt.git#fe04889a6c3fe73ccdb4b19b44ac62e2b1a96f7d
+// cbt:https://github.com/cvogt/cbt.git#f11b8318b85f16843d8cfa0743f64c1576614ad6
class Build...
```
diff --git a/cbt b/cbt
index 2fc9e07..65e405d 100755
--- a/cbt
+++ b/cbt
@@ -113,9 +113,13 @@ mkdir -p $NAILGUN$TARGET
nailgun_out=$NAILGUN/target/nailgun.stdout.log
nailgun_err=$NAILGUN/target/nailgun.strerr.log
+DEBUG=""
foo(){
while test $# -gt 0; do
case "$1" in
+ "-debug")
+ DEBUG="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
+ ;;
"-Dlog=nailgun")
nailgun_out=/dev/stderr
nailgun_err=/dev/stderr
@@ -193,9 +197,8 @@ stage1 () {
if [ $use_nailgun -eq 1 ]
then
log "Running JVM directly" "$@"
- # -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005
# JVM options to improve startup time. See https://github.com/cvogt/cbt/pull/262
- java $JAVA_OPTS -Xmx6072m -Xss10M -XX:MaxJavaStackTraceDepth=-1 -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xverify:none -cp $NAILGUN$TARGET cbt.NailgunLauncher $(time_taken) "$CWD" "$@"
+ java $JAVA_OPTS $DEBUG -Xmx6072m -Xss10M -XX:MaxJavaStackTraceDepth=-1 -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xverify:none -cp $NAILGUN$TARGET cbt.NailgunLauncher $(time_taken) "$CWD" "$@"
else
log "Running via background process (nailgun)" "$@"
for i in 0 1 2 3 4 5 6 7 8 9; do
diff --git a/circle.yml b/circle.yml
index 1615ad5..a25dc35 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,6 +1,6 @@
machine:
java:
- version: oraclejdk7
+ version: oraclejdk8
dependencies:
cache_directories:
@@ -13,5 +13,7 @@ dependencies:
test:
override:
- rm ~/.gitconfig # avoid url replacement breaking jgit
- - ./cbt direct test
- - ./cbt test
+ - ./cbt direct test:
+ timeout: 1800
+ - ./cbt test:
+ timeout: 1800
diff --git a/compatibility/Context.java b/compatibility/Context.java
index 1ec0f54..afd0b15 100644
--- a/compatibility/Context.java
+++ b/compatibility/Context.java
@@ -1,22 +1,43 @@
package cbt;
import java.io.*;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.*;
+import java.util.concurrent.*;
// TODO: try to reduce the number of members
-public abstract class Context{
+public interface Context{
+ // recently added methods that needs default values for old versions to work
+ public default long cbtLastModified(){
+ throw new IncompatibleCbtVersionException("You need to define method cbtLastModified.");
+ };
+ public default Map<Object,Object> persistentCache(){
+ throw new IncompatibleCbtVersionException("You need to define method persistentCache.");
+ };
+ public default Map<Object,Object> transientCache(){
+ throw new IncompatibleCbtVersionException("You need to define method transientCache.");
+ };
+ public default long start(){
+ throw new IncompatibleCbtVersionException("You need to define method start.");
+ };
+
+ // methods that exist for longer which every CBT version in use should have by now, no default values needed
public abstract File projectDirectory();
public abstract File cwd(); // REPLACE by something that allows to run cbt on some other directly
public abstract String[] argsArray(); // replace this by https://github.com/cvogt/cbt/issues/172 ?
public abstract String[] enabledLoggersArray();
- public abstract Long startCompat();
- public abstract Boolean cbtHasChangedCompat();
public abstract String scalaVersionOrNull(); // needed to propagate scalaVersion to dependendee builds
- public abstract ConcurrentHashMap<String,Object> permanentKeys();
- public abstract ConcurrentHashMap<Object,ClassLoader> permanentClassLoaders();
- public abstract ConcurrentHashMap<Object,Object> taskCache();
public abstract File cache();
public abstract File cbtHome();
public abstract File cbtRootHome(); // REMOVE
public abstract File compatibilityTarget(); // maybe replace this with search in the classloader for it?
public abstract BuildInterface parentBuildOrNull();
+
+ // deprecated methods
+ @java.lang.Deprecated
+ public abstract Long startCompat();
+ @java.lang.Deprecated
+ public abstract Boolean cbtHasChangedCompat();
+ @java.lang.Deprecated
+ public abstract ConcurrentHashMap<String,Object> permanentKeys();
+ @java.lang.Deprecated
+ public abstract ConcurrentHashMap<Object,ClassLoader> permanentClassLoaders();
}
diff --git a/compatibility/Dependency.java b/compatibility/Dependency.java
index d491174..1f719c2 100644
--- a/compatibility/Dependency.java
+++ b/compatibility/Dependency.java
@@ -2,9 +2,22 @@ package cbt;
import java.io.*;
public interface Dependency{
+ // recently added methods that needs default values for old versions to work
+ public default String moduleKey(){
+ throw new IncompatibleCbtVersionException("You need to define method moduleKey.");
+ };
+ public default long lastModified(){
+ throw new IncompatibleCbtVersionException("You need to define method lastModified.");
+ };
+
+ // methods that exist for longer which every CBT version in use should have by now, no default values needed
public abstract String show();
- public abstract Boolean needsUpdateCompat();
public abstract Dependency[] dependenciesArray();
public abstract File[] dependencyClasspathArray();
public abstract File[] exportedClasspathArray();
+
+ // deprecated methods
+ @java.lang.Deprecated
+ public abstract boolean needsUpdateCompat();
}
+
diff --git a/compatibility/IncompatibleCbtVersionException.java b/compatibility/IncompatibleCbtVersionException.java
new file mode 100644
index 0000000..dee50fb
--- /dev/null
+++ b/compatibility/IncompatibleCbtVersionException.java
@@ -0,0 +1,10 @@
+package cbt;
+
+public class IncompatibleCbtVersionException extends RuntimeException{
+ public IncompatibleCbtVersionException( String msg, Throwable parent ){
+ super( msg, parent );
+ }
+ public IncompatibleCbtVersionException( String msg ){
+ super( msg );
+ }
+}
diff --git a/compatibility/Result.java b/compatibility/Result.java
index 220aa3a..dadc3af 100644
--- a/compatibility/Result.java
+++ b/compatibility/Result.java
@@ -2,7 +2,7 @@
package cbt;
import java.io.*;
public interface Result<T>{
- public abstract Integer exitCode();
+ public abstract int exitCode();
public abstract OutputStream out();
public abstract OutputStream err();
public abstract InputStream in();
diff --git a/examples/scalajs-react-example/js/build/build.scala b/examples/scalajs-react-example/js/build/build.scala
index 29f1c73..f5fe33f 100644
--- a/examples/scalajs-react-example/js/build/build.scala
+++ b/examples/scalajs-react-example/js/build/build.scala
@@ -1,6 +1,6 @@
import cbt._
class Build(val context: Context) extends ScalaJsBuild{
- override val projectName = "my-project"
+ override def projectName = "my-project"
override def sources = super.sources ++ Seq(
projectDirectory.getParentFile ++ "/shared"
diff --git a/libraries/eval/build/build.scala b/libraries/eval/build/build.scala
index 8dcaabd..7135d3f 100644
--- a/libraries/eval/build/build.scala
+++ b/libraries/eval/build/build.scala
@@ -2,7 +2,7 @@ import cbt._
class Build(val context: Context) extends BaseBuild{
outer =>
override def dependencies = super.dependencies :+
- new ScalaCompilerDependency( context.cbtHasChanged, context.paths.mavenCache, scalaVersion )
+ new ScalaCompilerDependency( context.cbtLastModified, context.paths.mavenCache, scalaVersion )
override def test: Option[ExitCode] = Some{
new BasicBuild(context.copy(projectDirectory = projectDirectory ++ "/test")) with ScalaTest{
diff --git a/nailgun_launcher/BuildStage1Result.java b/nailgun_launcher/BuildStage1Result.java
index 312871d..64a660d 100644
--- a/nailgun_launcher/BuildStage1Result.java
+++ b/nailgun_launcher/BuildStage1Result.java
@@ -1,12 +1,14 @@
package cbt;
public class BuildStage1Result{
- public Boolean changed;
+ public long start;
+ public long stage1LastModified;
public ClassLoader classLoader;
public String stage1Classpath;
public String nailgunClasspath;
public String compatibilityClasspath;
- public BuildStage1Result( Boolean changed, ClassLoader classLoader, String stage1Classpath, String nailgunClasspath, String compatibilityClasspath ){
- this.changed = changed;
+ public BuildStage1Result( long start, long stage1LastModified, ClassLoader classLoader, String stage1Classpath, String nailgunClasspath, String compatibilityClasspath ){
+ this.start = start;
+ this.stage1LastModified = stage1LastModified;
this.classLoader = classLoader;
this.stage1Classpath = stage1Classpath;
this.nailgunClasspath = nailgunClasspath;
diff --git a/nailgun_launcher/CbtURLClassLoader.java b/nailgun_launcher/CbtURLClassLoader.java
index 38fc905..bf698b0 100644
--- a/nailgun_launcher/CbtURLClassLoader.java
+++ b/nailgun_launcher/CbtURLClassLoader.java
@@ -15,30 +15,29 @@ public class CbtURLClassLoader extends java.net.URLClassLoader{
+ "\n)"
);
}
- ClassLoaderCache2<Class> cache = new ClassLoaderCache2<Class>(
- new ConcurrentHashMap<String, Object>(),
- new ConcurrentHashMap<Object, Class>()
- );
+ ConcurrentHashMap<String,Class> cache = new ConcurrentHashMap<String,Class>();
public Class loadClass(String name) throws ClassNotFoundException{
Class _class = super.loadClass(name);
if(_class == null) throw new ClassNotFoundException(name);
else return _class;
}
- public Class loadClass(String name, Boolean resolve) throws ClassNotFoundException{
+ public Class loadClass(String name, boolean resolve) throws ClassNotFoundException{
//System.out.println("loadClass("+name+") on \n"+this);
- if(!cache.contains(name))
- try{
- cache.put(super.loadClass(name, resolve), name);
- } catch (ClassNotFoundException e){
- cache.put(Object.class, name);
+ synchronized( cache ){
+ if(!cache.containsKey(name))
+ try{
+ cache.put(name, super.loadClass(name, resolve));
+ } catch (ClassNotFoundException e){
+ cache.put(name, Object.class);
+ }
+ Class _class = cache.get(name);
+ if(_class == Object.class){
+ if( name.equals("java.lang.Object") )
+ return Object.class;
+ else return null;
+ } else {
+ return _class;
}
- Class _class = cache.get(name);
- if(_class == Object.class){
- if( name == "java.lang.Object" )
- return Object.class;
- else return null;
- } else {
- return _class;
}
}
void assertExist(URL[] urls){
diff --git a/nailgun_launcher/ClassLoaderCache.java b/nailgun_launcher/ClassLoaderCache.java
new file mode 100644
index 0000000..6bffad0
--- /dev/null
+++ b/nailgun_launcher/ClassLoaderCache.java
@@ -0,0 +1,66 @@
+package cbt;
+
+import java.util.*;
+import static java.io.File.pathSeparator;
+import static cbt.Stage0Lib.*;
+
+final public class ClassLoaderCache{
+ public Map<Object,Object> hashMap;
+ final ThreadLocal<HashSet<String>> seen = new ThreadLocal<HashSet<String>>(){
+ @Override protected HashSet<String> initialValue(){
+ return new HashSet<String>();
+ }
+ };
+
+ public ClassLoaderCache(
+ Map<Object,Object> hashMap
+ ){
+ this.hashMap = hashMap;
+ }
+
+ public ClassLoader get( String key, long timestamp ){
+ seen.get().add( key );
+ @SuppressWarnings("unchecked")
+ ClassLoader t = (ClassLoader) hashMap.get(
+ hashMap.get( key )
+ );
+ assert hashMap.get(t).equals(timestamp);
+ return t;
+ }
+
+ public boolean containsKey( String key, long timestamp ){
+ boolean contains = hashMap.containsKey( key );
+ if( contains ){
+ Object keyObject = hashMap.get( key );
+ Object classLoader = hashMap.get( keyObject );
+ long oldTimestamp = (long) hashMap.get( classLoader );
+ boolean res = oldTimestamp == timestamp;
+ return res;
+ } else {
+ return false;
+ }
+ }
+
+ public void put( String key, ClassLoader value, long timestamp ){
+ assert !seen.get().contains( key ): "Thread tries to update cache key after observing it: " + key;
+ LockableJavaKey keyObject = new LockableJavaKey();
+ hashMap.put( key, keyObject );
+ hashMap.put( keyObject, value );
+ hashMap.put( value, timestamp );
+ }
+
+ @Override public String toString(){
+ StringBuilder res = new StringBuilder();
+ res.append("ClassLoaderCache(\n\n");
+ for( Object key: hashMap.keySet() ){
+ if( key instanceof String )
+ res.append(
+ join( "\n", key.toString().split(":") ) + " -> " + hashMap.get( hashMap.get(key) )
+ + "\n\n"
+ );
+ }
+ res.append("\n\n");
+ return res.toString();
+ }
+}
+class LockableJavaKey{}
diff --git a/nailgun_launcher/ClassLoaderCache2.java b/nailgun_launcher/ClassLoaderCache2.java
deleted file mode 100644
index bf9ca3b..0000000
--- a/nailgun_launcher/ClassLoaderCache2.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package cbt;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import static java.io.File.pathSeparator;
-import static cbt.Stage0Lib.*;
-
-final class ClassLoaderCache2<T>{
- ConcurrentHashMap<String,Object> keys;
- ConcurrentHashMap<Object,T> values;
-
- public ClassLoaderCache2(
- ConcurrentHashMap<String,Object> keys,
- ConcurrentHashMap<Object,T> values
- ){
- this.keys = keys;
- this.values = values;
- }
-
- public T get( String key ){
- return values.get(
- keys.get( key )
- );
- }
-
- public Boolean contains( String key ){
- return keys.containsKey( key );
- }
-
- public T put( T value, String key ){
- LockableKey2 keyObject = new LockableKey2();
- keys.put( key, keyObject );
- values.put( keyObject, value );
- return value;
- }
-}
-class LockableKey2{} \ No newline at end of file
diff --git a/nailgun_launcher/EarlyDependencies.java b/nailgun_launcher/EarlyDependencies.java
index 4ffbdfd..8709e69 100644
--- a/nailgun_launcher/EarlyDependencies.java
+++ b/nailgun_launcher/EarlyDependencies.java
@@ -4,6 +4,7 @@ import java.io.*;
import java.nio.file.*;
import java.net.*;
import java.security.*;
+import java.util.*;
import static cbt.Stage0Lib.*;
import static cbt.NailgunLauncher.*;
@@ -19,125 +20,134 @@ class EarlyDependencies{
String scalaCompiler_2_11_8_File;
String scalaXml_1_0_5_File;
String scalaLibrary_2_11_8_File;
- String zinc_0_3_9_File;
- String incrementalCompiler_0_13_9_File;
- String compilerInterface_0_13_9_File;
- String scalaCompiler_2_10_5_File;
- String sbtInterface_0_13_9_File;
- String scalaReflect_2_10_5_File;
- String scalaLibrary_2_10_5_File;
+ String zinc_0_3_12_File;
+ String incrementalCompiler_0_13_12_File;
+ String compilerInterface_0_13_12_File;
+ String scalaCompiler_2_10_6_File;
+ String sbtInterface_0_13_12_File;
+ String scalaReflect_2_10_6_File;
+ String scalaLibrary_2_10_6_File;
public EarlyDependencies(
- String mavenCache, String mavenUrl, ClassLoaderCache2<ClassLoader> classLoaderCache, ClassLoader rootClassLoader
+ String mavenCache, String mavenUrl, ClassLoaderCache classLoaderCache, ClassLoader rootClassLoader
) throws Throwable {
scalaReflect_2_11_8_File = mavenCache + "/org/scala-lang/scala-reflect/2.11.8/scala-reflect-2.11.8.jar";
scalaCompiler_2_11_8_File = mavenCache + "/org/scala-lang/scala-compiler/2.11.8/scala-compiler-2.11.8.jar";
scalaXml_1_0_5_File = mavenCache + "/org/scala-lang/modules/scala-xml_2.11/1.0.5/scala-xml_2.11-1.0.5.jar";
scalaLibrary_2_11_8_File = mavenCache + "/org/scala-lang/scala-library/2.11.8/scala-library-2.11.8.jar";
- zinc_0_3_9_File = mavenCache + "/com/typesafe/zinc/zinc/0.3.9/zinc-0.3.9.jar";
- incrementalCompiler_0_13_9_File = mavenCache + "/com/typesafe/sbt/incremental-compiler/0.13.9/incremental-compiler-0.13.9.jar";
- compilerInterface_0_13_9_File = mavenCache + "/com/typesafe/sbt/compiler-interface/0.13.9/compiler-interface-0.13.9-sources.jar";
- scalaCompiler_2_10_5_File = mavenCache + "/org/scala-lang/scala-compiler/2.10.5/scala-compiler-2.10.5.jar";
- sbtInterface_0_13_9_File = mavenCache + "/com/typesafe/sbt/sbt-interface/0.13.9/sbt-interface-0.13.9.jar";
- scalaReflect_2_10_5_File = mavenCache + "/org/scala-lang/scala-reflect/2.10.5/scala-reflect-2.10.5.jar";
- scalaLibrary_2_10_5_File = mavenCache + "/org/scala-lang/scala-library/2.10.5/scala-library-2.10.5.jar";
+ zinc_0_3_12_File = mavenCache + "/com/typesafe/zinc/zinc/0.3.12/zinc-0.3.12.jar";
+ incrementalCompiler_0_13_12_File = mavenCache + "/com/typesafe/sbt/incremental-compiler/0.13.12/incremental-compiler-0.13.12.jar";
+ compilerInterface_0_13_12_File = mavenCache + "/com/typesafe/sbt/compiler-interface/0.13.12/compiler-interface-0.13.12-sources.jar";
+ scalaCompiler_2_10_6_File = mavenCache + "/org/scala-lang/scala-compiler/2.10.6/scala-compiler-2.10.6.jar";
+ sbtInterface_0_13_12_File = mavenCache + "/com/typesafe/sbt/sbt-interface/0.13.12/sbt-interface-0.13.12.jar";
+ scalaReflect_2_10_6_File = mavenCache + "/org/scala-lang/scala-reflect/2.10.6/scala-reflect-2.10.6.jar";
+ scalaLibrary_2_10_6_File = mavenCache + "/org/scala-lang/scala-library/2.10.6/scala-library-2.10.6.jar";
download(new URL(mavenUrl + "/org/scala-lang/scala-reflect/2.11.8/scala-reflect-2.11.8.jar"), Paths.get(scalaReflect_2_11_8_File), "b74530deeba742ab4f3134de0c2da0edc49ca361");
download(new URL(mavenUrl + "/org/scala-lang/scala-compiler/2.11.8/scala-compiler-2.11.8.jar"), Paths.get(scalaCompiler_2_11_8_File), "fe1285c9f7b58954c5ef6d80b59063569c065e9a");
- // org.scala-lang:scala-library:2.10.5
- download(new URL(mavenUrl + "/org/scala-lang/scala-library/2.10.5/scala-library-2.10.5.jar"), Paths.get(scalaLibrary_2_10_5_File), "57ac67a6cf6fd591e235c62f8893438e8d10431d");
-
- String[] scalaLibrary_2_10_5_ClasspathArray = new String[]{scalaLibrary_2_10_5_File};
- String scalaLibrary_2_10_5_Classpath = classpath( scalaLibrary_2_10_5_ClasspathArray );
- ClassLoader scalaLibrary_2_10_5_ =
- classLoaderCache.contains( scalaLibrary_2_10_5_Classpath )
- ? classLoaderCache.get( scalaLibrary_2_10_5_Classpath )
- : classLoaderCache.put( classLoader( scalaLibrary_2_10_5_File, rootClassLoader ), scalaLibrary_2_10_5_Classpath );
-
- // org.scala-lang:scala-reflect:2.10.5
- download(new URL(mavenUrl + "/org/scala-lang/scala-reflect/2.10.5/scala-reflect-2.10.5.jar"), Paths.get(scalaReflect_2_10_5_File), "7392facb48876c67a89fcb086112b195f5f6bbc3");
-
- String[] scalaReflect_2_10_5_ClasspathArray = new String[]{scalaLibrary_2_10_5_File, scalaReflect_2_10_5_File};
- String scalaReflect_2_10_5_Classpath = classpath( scalaReflect_2_10_5_ClasspathArray );
- ClassLoader scalaReflect_2_10_5_ =
- classLoaderCache.contains( scalaReflect_2_10_5_Classpath )
- ? classLoaderCache.get( scalaReflect_2_10_5_Classpath )
- : classLoaderCache.put( classLoader( scalaReflect_2_10_5_File, scalaLibrary_2_10_5_ ), scalaReflect_2_10_5_Classpath );
-
- // com.typesafe.sbt:sbt-interface:0.13.9
- download(new URL(mavenUrl + "/com/typesafe/sbt/sbt-interface/0.13.9/sbt-interface-0.13.9.jar"), Paths.get(sbtInterface_0_13_9_File), "29848631415402c81b732e919be88f268df37250");
-
- String[] sbtInterface_0_13_9_ClasspathArray = new String[]{sbtInterface_0_13_9_File, scalaLibrary_2_10_5_File, scalaReflect_2_10_5_File};
- String sbtInterface_0_13_9_Classpath = classpath( sbtInterface_0_13_9_ClasspathArray );
- ClassLoader sbtInterface_0_13_9_ =
- classLoaderCache.contains( sbtInterface_0_13_9_Classpath )
- ? classLoaderCache.get( sbtInterface_0_13_9_Classpath )
- : classLoaderCache.put( classLoader( sbtInterface_0_13_9_File, scalaReflect_2_10_5_ ), sbtInterface_0_13_9_Classpath );
-
- // org.scala-lang:scala-compiler:2.10.5
- download(new URL(mavenUrl + "/org/scala-lang/scala-compiler/2.10.5/scala-compiler-2.10.5.jar"), Paths.get(scalaCompiler_2_10_5_File), "f0f5bb444ca26a6e489af3dd35e24f7e2d2d118e");
-
- String[] scalaCompiler_2_10_5_ClasspathArray = new String[]{sbtInterface_0_13_9_File, scalaCompiler_2_10_5_File, scalaLibrary_2_10_5_File, scalaReflect_2_10_5_File};
- String scalaCompiler_2_10_5_Classpath = classpath( scalaCompiler_2_10_5_ClasspathArray );
- ClassLoader scalaCompiler_2_10_5_ =
- classLoaderCache.contains( scalaCompiler_2_10_5_Classpath )
- ? classLoaderCache.get( scalaCompiler_2_10_5_Classpath )
- : classLoaderCache.put( classLoader( scalaCompiler_2_10_5_File, sbtInterface_0_13_9_ ), scalaCompiler_2_10_5_Classpath );
-
- // com.typesafe.sbt:compiler-interface:0.13.9
- download(new URL(mavenUrl + "/com/typesafe/sbt/compiler-interface/0.13.9/compiler-interface-0.13.9-sources.jar"), Paths.get(compilerInterface_0_13_9_File), "2311addbed1182916ad00f83c57c0eeca1af382b");
-
- String[] compilerInterface_0_13_9_ClasspathArray = new String[]{compilerInterface_0_13_9_File, sbtInterface_0_13_9_File, scalaCompiler_2_10_5_File, scalaLibrary_2_10_5_File, scalaReflect_2_10_5_File};
- String compilerInterface_0_13_9_Classpath = classpath( compilerInterface_0_13_9_ClasspathArray );
- ClassLoader compilerInterface_0_13_9_ =
- classLoaderCache.contains( compilerInterface_0_13_9_Classpath )
- ? classLoaderCache.get( compilerInterface_0_13_9_Classpath )
- : classLoaderCache.put( classLoader( compilerInterface_0_13_9_File, scalaCompiler_2_10_5_ ), compilerInterface_0_13_9_Classpath );
-
- // com.typesafe.sbt:incremental-compiler:0.13.9
- download(new URL(mavenUrl + "/com/typesafe/sbt/incremental-compiler/0.13.9/incremental-compiler-0.13.9.jar"), Paths.get(incrementalCompiler_0_13_9_File), "fbbf1cadbed058aa226643e83543c35de43b13f0");
-
- String[] incrementalCompiler_0_13_9_ClasspathArray = new String[]{compilerInterface_0_13_9_File, incrementalCompiler_0_13_9_File, sbtInterface_0_13_9_File, scalaCompiler_2_10_5_File, scalaLibrary_2_10_5_File, scalaReflect_2_10_5_File};
- String incrementalCompiler_0_13_9_Classpath = classpath( incrementalCompiler_0_13_9_ClasspathArray );
- ClassLoader incrementalCompiler_0_13_9_ =
- classLoaderCache.contains( incrementalCompiler_0_13_9_Classpath )
- ? classLoaderCache.get( incrementalCompiler_0_13_9_Classpath )
- : classLoaderCache.put( classLoader( incrementalCompiler_0_13_9_File, compilerInterface_0_13_9_ ), incrementalCompiler_0_13_9_Classpath );
-
- // com.typesafe.zinc:zinc:0.3.9
- download(new URL(mavenUrl + "/com/typesafe/zinc/zinc/0.3.9/zinc-0.3.9.jar"), Paths.get(zinc_0_3_9_File), "46a4556d1f36739879f4b2cc19a73d12b3036e9a");
-
- String[] zinc_0_3_9_ClasspathArray = new String[]{compilerInterface_0_13_9_File, incrementalCompiler_0_13_9_File, sbtInterface_0_13_9_File, zinc_0_3_9_File, scalaCompiler_2_10_5_File, scalaLibrary_2_10_5_File, scalaReflect_2_10_5_File};
- String zinc_0_3_9_Classpath = classpath( zinc_0_3_9_ClasspathArray );
- ClassLoader zinc_0_3_9_ =
- classLoaderCache.contains( zinc_0_3_9_Classpath )
- ? classLoaderCache.get( zinc_0_3_9_Classpath )
- : classLoaderCache.put( classLoader( zinc_0_3_9_File, incrementalCompiler_0_13_9_ ), zinc_0_3_9_Classpath );
+ // org.scala-lang:scala-library:2.10.6
+ String[] scalaLibrary_2_10_6_ClasspathArray = new String[]{scalaLibrary_2_10_6_File};
+ ClassLoader scalaLibrary_2_10_6_ = loadDependency(
+ mavenUrl + "/org/scala-lang/scala-library/2.10.6/scala-library-2.10.6.jar",
+ scalaLibrary_2_10_6_File,
+ "421989aa8f95a05a4f894630aad96b8c7b828732",
+ classLoaderCache,
+ rootClassLoader,
+ scalaLibrary_2_10_6_ClasspathArray
+ );
+
+ // org.scala-lang:scala-reflect:2.10.6
+ String[] scalaReflect_2_10_6_ClasspathArray = new String[]{scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
+ ClassLoader scalaReflect_2_10_6_ = loadDependency(
+ mavenUrl + "/org/scala-lang/scala-reflect/2.10.6/scala-reflect-2.10.6.jar",
+ scalaReflect_2_10_6_File,
+ "3259f3df0f166f017ef5b2d385445808398c316c",
+ classLoaderCache,
+ scalaLibrary_2_10_6_,
+ scalaReflect_2_10_6_ClasspathArray
+ );
+
+ // com.typesafe.sbt:sbt-interface:0.13.12
+ String[] sbtInterface_0_13_12_ClasspathArray = new String[]{sbtInterface_0_13_12_File, scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
+ ClassLoader sbtInterface_0_13_12_ = loadDependency(
+ mavenUrl + "/com/typesafe/sbt/sbt-interface/0.13.12/sbt-interface-0.13.12.jar",
+ sbtInterface_0_13_12_File,
+ "fcc7875c02f0d4641fac0518121bd71475d3909b",
+ classLoaderCache,
+ scalaReflect_2_10_6_,
+ sbtInterface_0_13_12_ClasspathArray
+ );
+
+ // org.scala-lang:scala-compiler:2.10.6
+ String[] scalaCompiler_2_10_6_ClasspathArray = new String[]{sbtInterface_0_13_12_File, scalaCompiler_2_10_6_File, scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
+ ClassLoader scalaCompiler_2_10_6_ = loadDependency(
+ mavenUrl + "/org/scala-lang/scala-compiler/2.10.6/scala-compiler-2.10.6.jar",
+ scalaCompiler_2_10_6_File,
+ "9b15174852f5b6bb1edbf303d5722286a0a54011",
+ classLoaderCache,
+ sbtInterface_0_13_12_,
+ scalaCompiler_2_10_6_ClasspathArray
+ );
+
+ // com.typesafe.sbt:compiler-interface:0.13.12
+ String[] compilerInterface_0_13_12_ClasspathArray = new String[]{compilerInterface_0_13_12_File, sbtInterface_0_13_12_File, scalaCompiler_2_10_6_File, scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
+ ClassLoader compilerInterface_0_13_12_ = loadDependency(
+ mavenUrl + "/com/typesafe/sbt/compiler-interface/0.13.12/compiler-interface-0.13.12-sources.jar",
+ compilerInterface_0_13_12_File,
+ "d9c3270576e162bf017b146af262364c2db87a32",
+ classLoaderCache,
+ scalaCompiler_2_10_6_,
+ compilerInterface_0_13_12_ClasspathArray
+ );
+
+ // com.typesafe.sbt:incremental-compiler:0.13.12
+ String[] incrementalCompiler_0_13_12_ClasspathArray = new String[]{compilerInterface_0_13_12_File, incrementalCompiler_0_13_12_File, sbtInterface_0_13_12_File, scalaCompiler_2_10_6_File, scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
+ ClassLoader incrementalCompiler_0_13_12_ = loadDependency(
+ mavenUrl + "/com/typesafe/sbt/incremental-compiler/0.13.12/incremental-compiler-0.13.12.jar",
+ incrementalCompiler_0_13_12_File,
+ "259f6d24a5a3791bb233787d6a8e639c4ab86fe5",
+ classLoaderCache,
+ compilerInterface_0_13_12_,
+ incrementalCompiler_0_13_12_ClasspathArray
+ );
+
+ // com.typesafe.zinc:zinc:0.3.12
+ String[] zinc_0_3_12_ClasspathArray = new String[]{compilerInterface_0_13_12_File, incrementalCompiler_0_13_12_File, sbtInterface_0_13_12_File, zinc_0_3_12_File, scalaCompiler_2_10_6_File, scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
+ ClassLoader zinc_0_3_12_ = loadDependency(
+ mavenUrl + "/com/typesafe/zinc/zinc/0.3.12/zinc-0.3.12.jar",
+ zinc_0_3_12_File,
+ "c4339e93f5b7273f49ad026248f4fdb1d4d6c7c4",
+ classLoaderCache,
+ incrementalCompiler_0_13_12_,
+ zinc_0_3_12_ClasspathArray
+ );
// org.scala-lang:scala-library:2.11.8
- download(new URL(mavenUrl + "/org/scala-lang/scala-library/2.11.8/scala-library-2.11.8.jar"), Paths.get(scalaLibrary_2_11_8_File), "ddd5a8bced249bedd86fb4578a39b9fb71480573");
-
String[] scalaLibrary_2_11_8_ClasspathArray = new String[]{scalaLibrary_2_11_8_File};
- String scalaLibrary_2_11_8_Classpath = classpath( scalaLibrary_2_11_8_ClasspathArray );
- ClassLoader scalaLibrary_2_11_8_ =
- classLoaderCache.contains( scalaLibrary_2_11_8_Classpath )
- ? classLoaderCache.get( scalaLibrary_2_11_8_Classpath )
- : classLoaderCache.put( classLoader( scalaLibrary_2_11_8_File, rootClassLoader ), scalaLibrary_2_11_8_Classpath );
+ ClassLoader scalaLibrary_2_11_8_ = loadDependency(
+ mavenUrl + "/org/scala-lang/scala-library/2.11.8/scala-library-2.11.8.jar",
+ scalaLibrary_2_11_8_File,
+ "ddd5a8bced249bedd86fb4578a39b9fb71480573",
+ classLoaderCache,
+ rootClassLoader,
+ scalaLibrary_2_11_8_ClasspathArray
+ );
// org.scala-lang.modules:scala-xml_2.11:1.0.5
- download(new URL(mavenUrl + "/org/scala-lang/modules/scala-xml_2.11/1.0.5/scala-xml_2.11-1.0.5.jar"), Paths.get(scalaXml_1_0_5_File), "77ac9be4033768cf03cc04fbd1fc5e5711de2459");
-
String[] scalaXml_1_0_5_ClasspathArray = new String[]{scalaXml_1_0_5_File, scalaLibrary_2_11_8_File};
- String scalaXml_1_0_5_Classpath = classpath( scalaXml_1_0_5_ClasspathArray );
- ClassLoader scalaXml_1_0_5_ =
- classLoaderCache.contains( scalaXml_1_0_5_Classpath )
- ? classLoaderCache.get( scalaXml_1_0_5_Classpath )
- : classLoaderCache.put( classLoader( scalaXml_1_0_5_File, scalaLibrary_2_11_8_ ), scalaXml_1_0_5_Classpath );
-
+ ClassLoader scalaXml_1_0_5_ = loadDependency(
+ mavenUrl + "/org/scala-lang/modules/scala-xml_2.11/1.0.5/scala-xml_2.11-1.0.5.jar",
+ scalaXml_1_0_5_File,
+ "77ac9be4033768cf03cc04fbd1fc5e5711de2459",
+ classLoaderCache,
+ scalaLibrary_2_11_8_,
+ scalaXml_1_0_5_ClasspathArray
+ );
+
classLoader = scalaXml_1_0_5_;
classpathArray = scalaXml_1_0_5_ClasspathArray;
- zinc = zinc_0_3_9_;
+ zinc = zinc_0_3_12_;
}
}
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java
index 944daf8..d89c764 100644
--- a/nailgun_launcher/NailgunLauncher.java
+++ b/nailgun_launcher/NailgunLauncher.java
@@ -4,7 +4,6 @@ import java.lang.reflect.*;
import java.net.*;
import java.security.*;
import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
import static cbt.Stage0Lib.*;
import static java.io.File.pathSeparator;
@@ -15,10 +14,7 @@ import static java.io.File.pathSeparator;
*/
public class NailgunLauncher{
/** Persistent cache for caching classloaders for the JVM life time. */
- private final static ClassLoaderCache2<ClassLoader> classLoaderCache = new ClassLoaderCache2<ClassLoader>(
- new ConcurrentHashMap<String,Object>(),
- new ConcurrentHashMap<Object,ClassLoader>()
- );
+ private static Map<Object,Object> classLoaderCacheHashMap = new HashMap<Object,Object>();
public final static SecurityManager initialSecurityManager
= System.getSecurityManager();
@@ -30,14 +26,13 @@ public class NailgunLauncher{
@SuppressWarnings("unchecked")
public static Object getBuild( Object context ) throws Throwable{
BuildStage1Result res = buildStage1(
- (Boolean) get(context, "cbtHasChangedCompat"),
- (Long) get(context, "startCompat"),
+ (long) get(context, "cbtLastModified"),
+ (long) get(context, "start"),
((File) get(context, "cache")).toString() + "/",
((File) get(context, "cbtHome")).toString(),
((File) get(context, "compatibilityTarget")).toString() + "/",
- new ClassLoaderCache2<ClassLoader>(
- (ConcurrentHashMap<String,Object>) get(context, "permanentKeys"),
- (ConcurrentHashMap<Object,ClassLoader>) get(context, "permanentClassLoaders")
+ new ClassLoaderCache(
+ (HashMap) get(context, "persistentCache")
)
);
return
@@ -48,8 +43,16 @@ public class NailgunLauncher{
.invoke(null, context, res);
}
+ public static long nailgunLauncherLastModified = -1; // this initial value should be overwritten, never read
+
+ // wrap for caching
+ public static ClassLoader jdkClassLoader = new CbtURLClassLoader(
+ new URL[]{},
+ ClassLoader.getSystemClassLoader().getParent()
+ );
+
public static void main( String[] args ) throws Throwable {
- Long _start = System.currentTimeMillis();
+ long _start = System.currentTimeMillis();
if(args[0].equals("check-alive")){
System.exit(33);
return;
@@ -65,13 +68,13 @@ public class NailgunLauncher{
// scala.Console, which captured them at startup
try{
System.out.getClass().getDeclaredField("streams"); // nailgun ThreadLocalPrintStream
- assert(System.out.getClass().getName() == "com.martiansoftware.nailgun.ThreadLocalPrintStream");
+ assert(System.out.getClass().getName().equals("com.martiansoftware.nailgun.ThreadLocalPrintStream"));
} catch( NoSuchFieldException e ){
System.setOut( new PrintStream(new ThreadLocalOutputStream(System.out), true) );
}
try{
System.err.getClass().getDeclaredField("streams"); // nailgun ThreadLocalPrintStream
- assert(System.err.getClass().getName() == "com.martiansoftware.nailgun.ThreadLocalPrintStream");
+ assert(System.err.getClass().getName().equals("com.martiansoftware.nailgun.ThreadLocalPrintStream"));
} catch( NoSuchFieldException e ){
System.setErr( new PrintStream(new ThreadLocalOutputStream(System.err), true) );
}
@@ -81,28 +84,38 @@ public class NailgunLauncher{
String CBT_HOME = System.getenv("CBT_HOME");
String cache = CBT_HOME + "/cache/";
String compatibilityTarget = CBT_HOME + "/compatibility/" + TARGET;
+ // copy cache, so that this thread has a consistent view despite other threads
+ // changing their copies
+ // replace again before returning, see below
+ ClassLoaderCache classLoaderCache = new ClassLoaderCache(
+ new HashMap<Object,Object>(classLoaderCacheHashMap)
+ );
+
+ String nailgunTarget = CBT_HOME + "/" + NAILGUN + TARGET;
+ long nailgunLauncherLastModified = new File( nailgunTarget + "../classes.last-success" ).lastModified();
+
BuildStage1Result res = buildStage1(
- false, start, cache, CBT_HOME, compatibilityTarget, classLoaderCache
+ nailgunLauncherLastModified, start, cache, CBT_HOME, compatibilityTarget, classLoaderCache
);
try{
- System.exit(
- (Integer) res
- .classLoader
- .loadClass("cbt.Stage1")
- .getMethod(
- "run",
- String[].class, File.class, File.class, BuildStage1Result.class,
- Long.class, ConcurrentHashMap.class, ConcurrentHashMap.class
- )
- .invoke(
- null,
- (Object) args, new File(cache), new File(CBT_HOME), res,
- start, classLoaderCache.keys, classLoaderCache.values
- )
- );
+ int exitCode = (int) res
+ .classLoader
+ .loadClass("cbt.Stage1")
+ .getMethod(
+ "run", String[].class, File.class, File.class, BuildStage1Result.class, Map.class
+ ).invoke(
+ null, (Object) args, new File(cache), new File(CBT_HOME), res, classLoaderCache.hashMap
+ );
+
+ System.exit( exitCode );
} catch (java.lang.reflect.InvocationTargetException e) {
throw unwrapInvocationTargetException(e);
+ } finally {
+ // This replaces the cache and should be thread-safe.
+ // For competing threads the last one wins with a consistent cache.
+ // So worst case, we loose some of the cache that's replaced.
+ classLoaderCacheHashMap = classLoaderCache.hashMap;
}
}
@@ -115,7 +128,8 @@ public class NailgunLauncher{
}
public static BuildStage1Result buildStage1(
- Boolean changed, long start, String cache, String cbtHome, String compatibilityTarget, ClassLoaderCache2<ClassLoader> classLoaderCache
+ final long lastModified, final long start, final String cache, final String cbtHome,
+ final String compatibilityTarget, final ClassLoaderCache classLoaderCache
) throws Throwable {
_assert(TARGET != null, "environment variable TARGET not defined");
String nailgunTarget = cbtHome + "/" + NAILGUN + TARGET;
@@ -128,30 +142,32 @@ public class NailgunLauncher{
ClassLoader rootClassLoader = new CbtURLClassLoader( new URL[]{}, ClassLoader.getSystemClassLoader().getParent() ); // wrap for caching
EarlyDependencies earlyDeps = new EarlyDependencies(mavenCache, mavenUrl, classLoaderCache, rootClassLoader);
- ClassLoader compatibilityClassLoader;
+ long nailgunLauncherLastModified = new File( nailgunTarget + "../classes.last-success" ).lastModified();
+
+ long compatibilityLastModified;
if(!compatibilityTarget.startsWith(cbtHome)){
- compatibilityClassLoader = classLoaderCache.get( compatibilityTarget );
+ compatibilityLastModified = new File( compatibilityTarget + "../classes.last-success" ).lastModified();
} else {
List<File> compatibilitySourceFiles = new ArrayList<File>();
for( File f: compatibilitySources.listFiles() ){
- if( f.isFile() && (f.toString().endsWith(".scala") || f.toString().endsWith(".java")) ){
+ if( f.isFile() && f.toString().endsWith(".java") ){
compatibilitySourceFiles.add(f);
}
}
- changed = compile(changed, start, "", compatibilityTarget, earlyDeps, compatibilitySourceFiles);
-
- if( classLoaderCache.contains( compatibilityTarget ) ){
- compatibilityClassLoader = classLoaderCache.get( compatibilityTarget );
- } else {
- compatibilityClassLoader = classLoaderCache.put( classLoader(compatibilityTarget, rootClassLoader), compatibilityTarget );
+
+ compatibilityLastModified = compile( 0L, "", compatibilityTarget, earlyDeps, compatibilitySourceFiles);
+
+ if( !classLoaderCache.containsKey( compatibilityTarget, compatibilityLastModified ) ){
+ classLoaderCache.put( compatibilityTarget, classLoader(compatibilityTarget, rootClassLoader), compatibilityLastModified );
}
}
+ final ClassLoader compatibilityClassLoader = classLoaderCache.get( compatibilityTarget, compatibilityLastModified );
String[] nailgunClasspathArray = append( earlyDeps.classpathArray, nailgunTarget );
String nailgunClasspath = classpath( nailgunClasspathArray );
- ClassLoader nailgunClassLoader = new CbtURLClassLoader( new URL[]{}, NailgunLauncher.class.getClassLoader() ); // wrap for caching
- if( !classLoaderCache.contains( nailgunClasspath ) ){
- nailgunClassLoader = classLoaderCache.put( nailgunClassLoader, nailgunClasspath );
+ final ClassLoader nailgunClassLoader = new CbtURLClassLoader( new URL[]{}, NailgunLauncher.class.getClassLoader() ); // wrap for caching
+ if( !classLoaderCache.containsKey( nailgunClasspath, nailgunLauncherLastModified ) ){
+ classLoaderCache.put( nailgunClasspath, nailgunClassLoader, nailgunLauncherLastModified );
}
String[] stage1ClasspathArray =
@@ -164,14 +180,24 @@ public class NailgunLauncher{
stage1SourceFiles.add(f);
}
}
- changed = compile(changed, start, stage1Classpath, stage1Target, earlyDeps, stage1SourceFiles);
- ClassLoader stage1classLoader;
- if( !changed && classLoaderCache.contains( stage1Classpath ) ){
- stage1classLoader = classLoaderCache.get( stage1Classpath );
- } else {
- stage1classLoader =
+ final long stage1BeforeCompiled = System.currentTimeMillis();
+ final long stage0LastModified = Math.max(
+ lastModified,
+ Math.max( lastModified, compatibilityLastModified )
+ );
+ final long stage1LastModified = compile(
+ stage0LastModified, stage1Classpath, stage1Target, earlyDeps, stage1SourceFiles
+ );
+
+ if( stage1LastModified < compatibilityLastModified )
+ throw new AssertionError(
+ "Cache invalidation bug: cbt compatibility layer recompiled, but cbt stage1 did not."
+ );
+
+ if( !classLoaderCache.containsKey( stage1Classpath, stage1LastModified ) ){
classLoaderCache.put(
+ stage1Classpath,
classLoader(
stage1Target,
new MultiClassLoader2(
@@ -180,12 +206,14 @@ public class NailgunLauncher{
earlyDeps.classLoader
)
),
- stage1Classpath
+ stage1LastModified
);
}
+ final ClassLoader stage1classLoader = classLoaderCache.get( stage1Classpath, stage1LastModified );
return new BuildStage1Result(
- changed,
+ start,
+ stage1LastModified,
stage1classLoader,
stage1Classpath,
nailgunClasspath,
diff --git a/nailgun_launcher/Stage0Lib.java b/nailgun_launcher/Stage0Lib.java
index 452bdae..8237c3c 100644
--- a/nailgun_launcher/Stage0Lib.java
+++ b/nailgun_launcher/Stage0Lib.java
@@ -11,16 +11,17 @@ import static java.io.File.pathSeparator;
import static cbt.NailgunLauncher.*;
import java.nio.file.*;
import java.nio.file.attribute.FileTime;
+import static java.lang.Math.min;
public class Stage0Lib{
- public static void _assert(Boolean condition, Object msg){
+ public static void _assert(boolean condition, Object msg){
if(!condition){
throw new AssertionError("Assertion failed: "+msg);
}
}
public static int runMain(String cls, String[] args, ClassLoader cl) throws Throwable{
- Boolean trapExitCodeBefore = TrapSecurityManager.trapExitCode().get();
+ boolean trapExitCodeBefore = TrapSecurityManager.trapExitCode().get();
try{
TrapSecurityManager.trapExitCode().set(true);
cl.loadClass(cls)
@@ -47,33 +48,65 @@ public class Stage0Lib{
return join( pathSeparator, files );
}
+ public static long lastModified( String... files ){
+ List<Long> lastModified = new ArrayList<Long>();
+ for( String file: files ){
+ lastModified.add( new File(file).lastModified() );
+ }
+ return Collections.max( lastModified );
+ }
+
+ public static ClassLoader loadDependency(
+ String url,
+ String file,
+ String hash,
+ ClassLoaderCache classLoaderCache,
+ ClassLoader parent,
+ String... classpathArray
+ ) throws Throwable {
+ download(new URL(url), Paths.get(file), hash);
+
+ final long lastModified = lastModified( classpathArray );
+ final String classpath = classpath( classpathArray );
+
+ if( !classLoaderCache.containsKey( classpath, lastModified ) )
+ classLoaderCache.put( classpath, classLoader( file, parent ), lastModified );
+
+ return classLoaderCache.get( classpath, lastModified );
+ }
+
public static File write(File file, String content, OpenOption... options) throws Throwable{
file.getParentFile().mkdirs();
Files.write(file.toPath(), content.getBytes(), options);
return file;
}
- public static Boolean compile(
- Boolean changed, Long start, String classpath, String target,
+ public static long compile(
+ long lastModified, String classpath, String target,
EarlyDependencies earlyDeps, List<File> sourceFiles
) throws Throwable{
File statusFile = new File( new File(target) + ".last-success" );
- Long lastSuccessfullCompile = statusFile.lastModified();
+ long lastCompiled = statusFile.lastModified();
+
+ long maxLastModified = lastModified;
+ final long start = System.currentTimeMillis(); // <- before recursing, so we catch them all
+
for( File file: sourceFiles ){
- if( file.lastModified() > lastSuccessfullCompile ){
- changed = true;
- break;
- }
+ long l = file.lastModified();
+ if( l > maxLastModified ) maxLastModified = l;
+ // performance optimization because we'll recompile and don't need to check other files
+ if( l > lastCompiled ) break;
}
- if(changed){
+
+ if( maxLastModified > lastCompiled ){
List<String> zincArgs = new ArrayList<String>(
Arrays.asList(
new String[]{
"-scala-compiler", earlyDeps.scalaCompiler_2_11_8_File,
"-scala-library", earlyDeps.scalaLibrary_2_11_8_File,
"-scala-extra", earlyDeps.scalaReflect_2_11_8_File,
- "-sbt-interface", earlyDeps.sbtInterface_0_13_9_File,
- "-compiler-interface", earlyDeps.compilerInterface_0_13_9_File,
+ "-sbt-interface", earlyDeps.sbtInterface_0_13_12_File,
+ "-compiler-interface", earlyDeps.compilerInterface_0_13_12_File,
"-cp", classpath,
"-d", target,
"-S-deprecation",
@@ -100,8 +133,10 @@ public class Stage0Lib{
} finally {
System.setOut(oldOut);
}
+ return statusFile.lastModified(); // can't just use `start` here as system time precision is less than milliseconds on OSX
+ } else {
+ return lastCompiled;
}
- return changed;
}
public static ClassLoader classLoader( String file ) throws Throwable{
@@ -117,7 +152,7 @@ public class Stage0Lib{
private static String getVarFromEnv(String envKey) {
String value = System.getenv(envKey);
- if(value==null || value.isEmpty()) {
+ if(value == null || value.isEmpty()) {
value = System.getenv(envKey.toUpperCase());
}
return value;
@@ -126,7 +161,7 @@ public class Stage0Lib{
private static void setProxyfromPropOrEnv(String envKey, String propKeyH, String propKeyP) {
String proxyHost = System.getProperty(propKeyH);
String proxyPort = System.getProperty(propKeyP);
- if((proxyHost==null || proxyHost.isEmpty()) && (proxyPort==null || proxyPort.isEmpty())) {
+ if((proxyHost == null || proxyHost.isEmpty()) && (proxyPort == null || proxyPort.isEmpty())) {
String envVar = getVarFromEnv(envKey);
if(envVar != null && !envVar.isEmpty()) {
String[] proxy = envVar.replaceFirst("^https?://", "").split(":", 2);
@@ -140,7 +175,7 @@ public class Stage0Lib{
setProxyfromPropOrEnv("http_proxy", "http.proxyHost", "http.proxyPort");
setProxyfromPropOrEnv("https_proxy", "https.proxyHost", "https.proxyPort");
String nonHosts = System.getProperty("http.nonProxyHosts");
- if(nonHosts==null || nonHosts.isEmpty()) {
+ if(nonHosts == null || nonHosts.isEmpty()) {
String envVar = getVarFromEnv("no_proxy");
if(envVar != null && !envVar.isEmpty()) {
System.setProperty("http.nonProxyHosts", envVar.replaceAll(",","|"));
diff --git a/nailgun_launcher/TrapSecurityManager.java b/nailgun_launcher/TrapSecurityManager.java
index 48e152b..be59671 100644
--- a/nailgun_launcher/TrapSecurityManager.java
+++ b/nailgun_launcher/TrapSecurityManager.java
@@ -39,7 +39,7 @@ public class TrapSecurityManager extends ProxySecurityManager{
public TrapSecurityManager(){
super(NailgunLauncher.initialSecurityManager);
}
-
+
public void checkPermission( Permission permission ){
/*
NOTE: is it actually ok, to just make these empty?
diff --git a/plugins/essentials/DynamicOverrides.scala b/plugins/essentials/DynamicOverrides.scala
index 4a3fe9e..1050f98 100644
--- a/plugins/essentials/DynamicOverrides.scala
+++ b/plugins/essentials/DynamicOverrides.scala
@@ -1,10 +1,12 @@
package cbt
import cbt.eval.Eval
trait DynamicOverrides extends BaseBuild{
- private val twitterEval = cached("eval"){
- new Eval{
- override lazy val impliedClassPath: List[String] = context.parentBuild.get.classpath.strings.toList//new ScalaCompilerDependency( context.cbtHasChanged, context.paths.mavenCache, scalaVersion ).classpath.strings.toList
- override def classLoader = DynamicOverrides.this.getClass.getClassLoader
+ private val twitterEval = {
+ taskCache[DynamicOverrides]( "eval" ).memoize{
+ new Eval{
+ override lazy val impliedClassPath: List[String] = context.parentBuild.get.classpath.strings.toList//new ScalaCompilerDependency( context.cbtLastModified, context.paths.mavenCache, scalaVersion ).classpath.strings.toList
+ override def classLoader = DynamicOverrides.this.getClass.getClassLoader
+ }
}
}
diff --git a/plugins/scalajs/ScalaJsBuild.scala b/plugins/scalajs/ScalaJsBuild.scala
index 9374f66..99f8616 100644
--- a/plugins/scalajs/ScalaJsBuild.scala
+++ b/plugins/scalajs/ScalaJsBuild.scala
@@ -6,7 +6,7 @@ trait ScalaJsBuild extends BaseBuild {
final protected val scalaJsLib = ScalaJsLib(
scalaJsVersion,
scalaVersion,
- context.cbtHasChanged,
+ context.cbtLastModified,
context.classLoaderCache,
context.paths.mavenCache
)
diff --git a/plugins/scalajs/ScalaJsLib.scala b/plugins/scalajs/ScalaJsLib.scala
index 0355850..f500039 100644
--- a/plugins/scalajs/ScalaJsLib.scala
+++ b/plugins/scalajs/ScalaJsLib.scala
@@ -3,8 +3,8 @@ import java.io.File
case class ScalaJsLib(
scalaJsVersion: String, scalaVersion: String,
- cbtHasChanged: Boolean, classLoaderCache: ClassLoaderCache, mavenCache: File
-)(implicit logger: Logger){
+ cbtLastModified: Long, classLoaderCache: ClassLoaderCache, mavenCache: File
+)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]){
sealed trait ScalaJsOutputMode {
def option: String
def fileSuffix: String
@@ -19,7 +19,7 @@ case class ScalaJsLib(
}
val lib = new Lib(logger)
- def dep(artifactId: String) = MavenResolver( cbtHasChanged, mavenCache, mavenCentral ).bindOne(
+ def dep(artifactId: String) = MavenResolver( cbtLastModified, mavenCache, mavenCentral ).bindOne(
MavenDependency("org.scala-js", artifactId, scalaJsVersion)
)
diff --git a/plugins/scalatest/ScalaTest.scala b/plugins/scalatest/ScalaTest.scala
index ee96431..5ccabc6 100644
--- a/plugins/scalatest/ScalaTest.scala
+++ b/plugins/scalatest/ScalaTest.scala
@@ -6,7 +6,7 @@ trait ScalaTest extends BaseBuild{
override def run: ExitCode = {
import ScalaTestLib._
val _classLoader = classLoader(context.classLoaderCache)
- val suiteNames = compile.map( d => discoverSuites(d, _classLoader) ).toVector.flatten
+ val suiteNames = compileFile.map( d => discoverSuites(d, _classLoader) ).toVector.flatten
runSuites( suiteNames.map( loadSuite( _, _classLoader ) ) )
ExitCode.Success
}
diff --git a/plugins/wartremover/WartRemover.scala b/plugins/wartremover/WartRemover.scala
index d5bbcd0..9cf8851 100644
--- a/plugins/wartremover/WartRemover.scala
+++ b/plugins/wartremover/WartRemover.scala
@@ -10,7 +10,7 @@ trait WartRemover extends BaseBuild {
private[this] def wartremoverCompilerDependency: String =
MavenResolver(
- context.cbtHasChanged,
+ context.cbtLastModified,
context.paths.mavenCache,
mavenCentral).bindOne(
ScalaDependency("org.wartremover", "wartremover", "1.1.1")
diff --git a/stage1/Cache.scala b/stage1/Cache.scala
deleted file mode 100644
index a8036e5..0000000
--- a/stage1/Cache.scala
+++ /dev/null
@@ -1,14 +0,0 @@
-package cbt
-/**
-Caches exactly one value.
-Is there a less boiler-platy way to achieve this, that doesn't
-require creating an instance for each thing you want to cache?
-*/
-class Cache[T]{
- private var value: Option[T] = None
- def apply(value: => T) = this.synchronized{
- if(!this.value.isDefined)
- this.value = Some(value)
- this.value.get
- }
-}
diff --git a/stage1/CachingClassLoader.scala b/stage1/CachingClassLoader.scala
index 4ddebda..38c0a1b 100644
--- a/stage1/CachingClassLoader.scala
+++ b/stage1/CachingClassLoader.scala
@@ -5,7 +5,7 @@ import scala.util.Try
trait CachingClassLoader extends ClassLoader{
def logger: Logger
- val cache = new KeyLockedLazyCache[String,Option[Class[_]]]( new ConcurrentHashMap, new ConcurrentHashMap, Some(logger) )
+ val cache = new KeyLockedLazyCache[Option[Class[_]]]( new ConcurrentHashMap[AnyRef,AnyRef], Some(logger) )
override def loadClass(name: String, resolve: Boolean) = {
cache.get( name, Try(super.loadClass(name, resolve)).toOption ).getOrElse(null)
}
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 e430ee1..0000000
--- a/stage1/ClassLoaderCache.scala
+++ /dev/null
@@ -1,24 +0,0 @@
-package cbt
-
-import java.net._
-import java.util.concurrent.ConcurrentHashMap
-import collection.JavaConversions._
-
-case class ClassLoaderCache(
- logger: Logger,
- private[cbt] permanentKeys: ConcurrentHashMap[String,AnyRef],
- private[cbt] permanentClassLoaders: ConcurrentHashMap[AnyRef,ClassLoader]
-){
- val persistent = new KeyLockedLazyCache(
- permanentKeys,
- permanentClassLoaders,
- Some(logger)
- )
- override def toString = (
- s"ClassLoaderCache("
- ++
- persistent.keys.keySet.toVector.map(_.toString.split(":").mkString("\n")).sorted.mkString("\n\n","\n\n","\n\n")
- ++
- ")"
- )
-}
diff --git a/stage1/ContextImplementation.scala b/stage1/ContextImplementation.scala
index 152e606..69094b0 100644
--- a/stage1/ContextImplementation.scala
+++ b/stage1/ContextImplementation.scala
@@ -1,22 +1,28 @@
package cbt
import java.io._
-import java.util.concurrent.ConcurrentHashMap
-import java.lang._
-case class ContextImplementation(
- projectDirectory: File,
- cwd: File,
- argsArray: Array[String],
- enabledLoggersArray: Array[String],
- startCompat: Long,
- cbtHasChangedCompat: Boolean,
- scalaVersionOrNull: String,
- permanentKeys: ConcurrentHashMap[String,AnyRef],
- permanentClassLoaders: ConcurrentHashMap[AnyRef,ClassLoader],
- taskCache: ConcurrentHashMap[AnyRef,AnyRef],
- cache: File,
- cbtHome: File,
- cbtRootHome: File,
- compatibilityTarget: File,
- parentBuildOrNull: BuildInterface
-) extends Context \ No newline at end of file
+class ContextImplementation(
+ 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/KeyLockedLazyCache.scala b/stage1/KeyLockedLazyCache.scala
index 4eff5b2..aac32a3 100644
--- a/stage1/KeyLockedLazyCache.scala
+++ b/stage1/KeyLockedLazyCache.scala
@@ -1,62 +1,67 @@
package cbt
-import java.util.concurrent.ConcurrentHashMap
-
private[cbt] class LockableKey
/**
-A cache that lazily computes values if needed during lookup.
+A hashMap that lazily computes values if needed during lookup.
Locking occurs on the key, so separate keys can be looked up
simultaneously without a deadlock.
*/
-final private[cbt] class KeyLockedLazyCache[Key <: AnyRef,Value <: AnyRef](
- val keys: ConcurrentHashMap[Key,AnyRef],
- val values: ConcurrentHashMap[AnyRef,Value],
+final private[cbt] class KeyLockedLazyCache[T <: AnyRef](
+ val hashMap: java.util.Map[AnyRef,AnyRef],
logger: Option[Logger]
){
- def get( key: Key, value: => Value ): Value = {
- val lockableKey = keys.synchronized{
- if( ! (keys containsKey key) ){
+ final val seen = new ThreadLocal[collection.mutable.Set[AnyRef]](){
+ override protected def initialValue = collection.mutable.Set[AnyRef]();
+ }
+ def get( key: AnyRef, value: => T ): T = {
+ seen.get.add( key );
+ val lockableKey = hashMap.synchronized{
+ if( ! (hashMap containsKey key) ){
val lockableKey = new LockableKey
//logger.foreach(_.resolver("CACHE MISS: " ++ key.toString))
- keys.put( key, lockableKey )
+ hashMap.put( key, lockableKey )
lockableKey
} else {
- val lockableKey = keys get key
+ val lockableKey = hashMap get key
//logger.foreach(_.resolver("CACHE HIT: " ++ lockableKey.toString ++ " -> " ++ key.toString))
lockableKey
}
}
import collection.JavaConversions._
- //logger.resolver("CACHE: \n" ++ keys.mkString("\n"))
+ //logger.resolver("CACHE: \n" ++ hashMap.mkString("\n"))
// synchronizing on key only, so asking for a particular key does
- // not block the whole cache, but just that cache entry
+ // not block the whole hashMap, but just that hashMap entry
lockableKey.synchronized{
- if( ! (values containsKey lockableKey) ){
- values.put( lockableKey, value )
+ if( ! (hashMap containsKey lockableKey) ){
+ hashMap.put( lockableKey, value )
}
- values get lockableKey
+ (hashMap get lockableKey).asInstanceOf[T]
}
}
- def update( key: Key, value: Value ): Value = {
- val lockableKey = keys get key
+ def update( key: AnyRef, value: T ): T = {
+ assert(
+ !seen.get.contains( key ),
+ "Thread tries to update cache key after observing it: " + key
+ )
+ val lockableKey = hashMap get key
lockableKey.synchronized{
- values.put( lockableKey, value )
+ hashMap.put( lockableKey, value )
value
}
}
- def remove( key: Key ) = keys.synchronized{
- assert(keys containsKey key)
- val lockableKey = keys get key
+ def remove( key: AnyRef ) = hashMap.synchronized{
+ assert(hashMap containsKey key)
+ val lockableKey = hashMap get key
lockableKey.synchronized{
- if(values containsKey lockableKey){
- // this is so values in the process of being replaced (which mean they have a key but no value)
+ if(hashMap containsKey lockableKey){
+ // this is so hashMap in the process of being replaced (which mean they have a key but no value)
// are not being removed
- keys.remove( key )
- values.remove( lockableKey )
+ hashMap.remove( key )
+ hashMap.remove( lockableKey )
}
}
}
- def containsKey( key: Key ) = keys.synchronized{
- keys containsKey key
+ def containsKey( key: AnyRef ) = hashMap.synchronized{
+ hashMap containsKey key
}
}
diff --git a/stage1/MavenRepository.scala b/stage1/MavenRepository.scala
index 4184d2d..a8c9b51 100644
--- a/stage1/MavenRepository.scala
+++ b/stage1/MavenRepository.scala
@@ -1,9 +1,13 @@
package cbt
import java.io._
import java.net._
-case class MavenResolver( cbtHasChanged: Boolean, mavenCache: File, urls: URL* ){
- def bind( dependencies: MavenDependency* )(implicit logger: Logger): Seq[BoundMavenDependency]
- = dependencies.map( BoundMavenDependency(cbtHasChanged,mavenCache,_,urls.to) ).to
- def bindOne( dependency: MavenDependency )(implicit logger: Logger): BoundMavenDependency
- = BoundMavenDependency( cbtHasChanged, mavenCache, dependency, urls.to )
+case class MavenResolver(
+ cbtLastModified: Long, mavenCache: File, urls: URL*
+)(
+ implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]
+){
+ def bind( dependencies: MavenDependency* ): Seq[BoundMavenDependency]
+ = dependencies.map( BoundMavenDependency(cbtLastModified,mavenCache,_,urls.to) ).to
+ def bindOne( dependency: MavenDependency ): BoundMavenDependency
+ = BoundMavenDependency( cbtLastModified, mavenCache, dependency, urls.to )
}
diff --git a/stage1/PoorMansProfiler.scala b/stage1/PoorMansProfiler.scala
index b7aa47d..4bc44ba 100644
--- a/stage1/PoorMansProfiler.scala
+++ b/stage1/PoorMansProfiler.scala
@@ -1,10 +1,10 @@
/*
// temporary debugging tool
package cbt
-import java.util.concurrent.ConcurrentHashMap
+import java.util._
import collection.JavaConversions._
object PoorMansProfiler{
- val entries = new ConcurrentHashMap[String, Long]
+ val entries = new HashMap[String, Long]
def profile[T](name: String)(code: => T): T = {
val before = System.currentTimeMillis
if(!(entries containsKey name)){
diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala
index d559dd4..bf6a3d4 100644
--- a/stage1/Stage1.scala
+++ b/stage1/Stage1.scala
@@ -1,7 +1,7 @@
package cbt
import java.io._
-import java.util.concurrent.ConcurrentHashMap
+import java.util._
import scala.collection.JavaConverters._
@@ -33,24 +33,24 @@ final case class Stage1ArgsParser(__args: Seq[String]) {
abstract class Stage2Base{
- def run( context: Stage2Args ): Unit
+ def run( context: Stage2Args ): ExitCode
}
case class Stage2Args(
cwd: File,
args: Seq[String],
- cbtHasChanged: Boolean,
+ stage2LastModified: Long,
+ logger: Logger,
classLoaderCache: ClassLoaderCache,
cache: File,
cbtHome: File,
compatibilityTarget: File
+)(
+ implicit val transientCache: java.util.Map[AnyRef,AnyRef]
){
- val ClassLoaderCache(
- logger,
- permanentKeys,
- permanentClassLoaders
- ) = classLoaderCache
+ val persistentCache = classLoaderCache.hashMap
}
+
object Stage1{
protected def newerThan( a: File, b: File ) ={
a.lastModified > b.lastModified
@@ -58,17 +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.permanentKeys,
- context.permanentClassLoaders
- ),
+ new ClassLoaderCache( context.persistentCache ),
context.cbtHome,
- context.cache
- )
+ context.cache,
+ logger
+ )(context.transientCache)
classLoader
.loadClass("cbt.Stage2")
@@ -76,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
- ): (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._
@@ -94,62 +92,59 @@ object Stage1{
val stage2sourceFiles = (
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 = 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.dependencyClasspath,
+ 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.persistent.containsKey( cbtDependency.classpath.string ) ) {
- classLoaderCache.persistent.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.persistent.containsKey( cbtDependency.stage1Dependency.compatibilityDependency.classpath.string ),
- "cbt unchanged, expected compatibility classloader to be cached"
- )
- assert(
- classLoaderCache.persistent.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(
@@ -158,7 +153,7 @@ object Stage1{
)
}
- ( cbtHasChanged, stage2ClassLoader )
+ ( stage2LastModified, stage2ClassLoader )
}
def run(
@@ -166,28 +161,23 @@ object Stage1{
cache: File,
cbtHome: File,
buildStage1: BuildStage1Result,
- start: java.lang.Long,
- classLoaderCacheKeys: ConcurrentHashMap[String,AnyRef],
- classLoaderCacheValues: ConcurrentHashMap[AnyRef,ClassLoader]
+ 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,
- classLoaderCacheKeys,
- classLoaderCacheValues
- )
-
+ 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 273b9af..f73995e 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -9,7 +9,6 @@ import java.nio.file.attribute.FileTime
import javax.tools._
import java.security._
import java.util.{Set=>_,Map=>_,List=>_,_}
-import java.util.concurrent.ConcurrentHashMap
import javax.xml.bind.annotation.adapters.HexBinaryAdapter
// CLI interop
@@ -33,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
@@ -101,7 +100,7 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
trapExitCode{
val c = classLoader.loadClass(cls)
val m = c.getMethod( "main", classOf[Array[String]] )
- val instance =
+ val instance =
if(!fakeInstance) null else c.newInstance
assert(
fakeInstance || (m.getModifiers & java.lang.reflect.Modifier.STATIC) > 0,
@@ -184,37 +183,37 @@ 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,
- classpath: ClassPath,
+ dependencies: Seq[Dependency],
mavenCache: File,
scalacOptions: Seq[String] = Seq(),
classLoaderCache: ClassLoaderCache,
zincVersion: String,
scalaVersion: String
- ): Option[File] = {
-
+ )(
+ implicit transientCache: java.util.Map[AnyRef, AnyRef]
+ ): 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 @
@@ -241,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(
@@ -255,7 +252,7 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
)
val singleArgs = scalacOptions.map( "-S" ++ _ )
- val code =
+ val code =
redirectOutToErr{
System.err.println("Compiling to " ++ compileTarget.toString)
try{
@@ -263,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 {
@@ -282,9 +279,11 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
-cp \\
${classpath.strings.mkString(":\\\n")} \\
\\
- ${files.sorted.mkString(" \\\n")}
+ ${sourceFiles.sorted.mkString(" \\\n")}
"""
)
+
+ redirectOutToErr( e.printStackTrace )
ExitCode.Failure
}
}
@@ -297,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 = {
@@ -357,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 )
@@ -377,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
@@ -416,32 +417,61 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{
}
def classLoaderRecursion( dependency: Dependency, latest: Map[(String,String),Dependency], cache: ClassLoaderCache ): ClassLoader = {
- val d = dependency
+ // FIXME: shouldn't we be using KeyLockedLazyCache instead of hashmap directly here?
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() )
+ NailgunLauncher.jdkClassLoader
} else if( dependencies.size == 1 ){
classLoaderRecursion( dependencies.head, latest, cache )
} else{
- val cp = d.dependencyClasspath.string
- if( dependencies.exists(_.needsUpdate) && cache.persistent.containsKey(cp) ){
- cache.persistent.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.persistent.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.persistent.get( a.classpath.string, cl )
+ 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 )
+ }
+
+}
+
+import scala.reflect._
+import scala.language.existentials
+case class PerClassCache(cache: java.util.Map[AnyRef,AnyRef], moduleKey: String)(implicit logger: Logger){
+ def apply[D <: Dependency: ClassTag](key: AnyRef): MethodCache[D] = new MethodCache[D](key)
+ case class MethodCache[D <: Dependency: ClassTag](key: AnyRef){
+ def memoize[T <: AnyRef](task: => T): T = {
+ val fullKey = (classTag[D].runtimeClass, moduleKey, key)
+ logger.transientCache("fetching key"+fullKey)
+ if( cache.containsKey(fullKey) ){
+ logger.transientCache("found key"+fullKey)
+ cache.get(fullKey).asInstanceOf[T]
+ } else{
+ val value = task
+ logger.transientCache("put key"+fullKey)
+ cache.put( fullKey, value )
+ value
+ }
+ }
}
}
diff --git a/stage1/cbt.scala b/stage1/cbt.scala
index e324fa0..0b0ccbf 100644
--- a/stage1/cbt.scala
+++ b/stage1/cbt.scala
@@ -2,7 +2,6 @@ package cbt
import java.io._
import java.nio.file._
import java.net._
-import java.util.concurrent.ConcurrentHashMap
object `package`{
implicit class TypeInferenceSafeEquals[T](value: T){
@@ -34,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
@@ -53,47 +57,43 @@ 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,
- permanentKeys,
- permanentClassLoaders
- )
- def cbtDependency = {
+
+ def classLoaderCache: ClassLoaderCache = new ClassLoaderCache( persistentCache )
+ def cbtDependencies = {
import paths._
- CbtDependency(cbtHasChanged, mavenCache, nailgunTarget, stage1Target, stage2Target, compatibilityTarget)
+ 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
- ): Context = ContextImplementation(
+ ): Context = new ContextImplementation(
projectDirectory,
cwd,
args.to,
enabledLoggers.to,
- startCompat,
- cbtHasChangedCompat,
+ start,
+ cbtLastModified,
scalaVersion.getOrElse(null),
- permanentKeys,
- permanentClassLoaders,
- taskCache,
+ persistentCache,
+ transientCache,
cache,
cbtHome,
cbtRootHome,
diff --git a/stage1/logger.scala b/stage1/logger.scala
index effdc35..8c8431a 100644
--- a/stage1/logger.scala
+++ b/stage1/logger.scala
@@ -41,6 +41,7 @@ case class Logger(enabledLoggers: Set[String], start: Long) {
final def git(msg: => String) = log(names.git, msg)
final def pom(msg: => String) = log(names.pom, msg)
final def dynamic(msg: => String) = log(names.dynamic, msg)
+ final def transientCache(msg: => String) = log(names.transientCache, msg)
private object names{
val stage1 = "stage1"
@@ -54,6 +55,7 @@ case class Logger(enabledLoggers: Set[String], start: Long) {
val pom = "pom"
val git = "git"
val dynamic = "dynamic"
+ val transientCache = "transientCache"
}
private def logUnguarded(name: String, msg: => String) = {
diff --git a/stage1/resolver.scala b/stage1/resolver.scala
index ff5ad68..e6e4588 100644
--- a/stage1/resolver.scala
+++ b/stage1/resolver.scala
@@ -4,29 +4,28 @@ import java.nio.charset.StandardCharsets
import java.net._
import java.io._
import scala.xml._
-import scala.concurrent._
-import scala.concurrent.duration._
trait DependencyImplementation extends Dependency{
implicit protected def logger: Logger
protected def lib = new Stage1Lib(logger)
+ implicit protected def transientCache: java.util.Map[AnyRef,AnyRef]
+ /** key used by taskCache to identify different objects that represent the same logical module */
+ protected def moduleKey: String
/**
- 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
+ caches given value in context keyed with given key and projectDirectory
+ the context is fresh on every complete run of cbt
+ */
+ protected lazy val taskCache = new PerClassCache(transientCache, moduleKey)
+
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]]
@@ -103,11 +102,11 @@ trait DependencyImplementation extends Dependency{
)
def dependencies: Seq[Dependency]
- private object transitiveDependenciesCache extends Cache[Seq[Dependency]]
/** return dependencies in order of linearized dependence. this is a bit tricky. */
- def transitiveDependencies: Seq[Dependency] = transitiveDependenciesCache{
- lib.transitiveDependencies(this)
- }
+ def transitiveDependencies: Seq[Dependency] =
+ taskCache[DependencyImplementation]( "transitiveDependencies" ).memoize{
+ lib.transitiveDependencies(this)
+ }
override def show: String = this.getClass.getSimpleName
// ========== debug ==========
@@ -115,64 +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) 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) 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) 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))
-case class ScalaDependencies(cbtHasChanged: Boolean, mavenCache: File, version: String)(implicit val logger: Logger) extends DependencyImplementation{ sd =>
- override final val needsUpdate = false
- def targetClasspath = ClassPath()
- def exportedClasspath = ClassPath()
- def dependencies = Seq(
- new ScalaCompilerDependency(cbtHasChanged, mavenCache, version),
- new ScalaLibraryDependency(cbtHasChanged, mavenCache, version),
- new ScalaReflectDependency(cbtHasChanged, mavenCache, version)
+class ScalaDependencies(cbtLastModified: Long, mavenCache: File, version: String)(implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]) extends Dependencies(
+ Seq(
+ new ScalaCompilerDependency(cbtLastModified, mavenCache, version),
+ new ScalaLibraryDependency(cbtLastModified, mavenCache, version),
+ new ScalaReflectDependency(cbtLastModified, mavenCache, version)
)
-}
+)
-case class BinaryDependency( path: File, dependencies: Seq[Dependency] )(implicit val logger: Logger) extends DependencyImplementation{
- def exportedClasspath = ClassPath(Seq(path))
- override def needsUpdate = false
+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 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) extends DependencyImplementation{
- override def needsUpdate = dependencies.exists(_.needsUpdate)
- override def exportedClasspath = ClassPath()
- override def targetClasspath = ClassPath()
+case class Dependencies( dependencies: Seq[Dependency] )(implicit val logger: Logger, val transientCache: java.util.Map[AnyRef,AnyRef]) extends DependencyImplementation{
+ override def lastModified = dependencies.map(_.lastModified).maxOption.getOrElse(0)
+ def moduleKey = this.getClass.getName ++ "(" ++ dependencies.map(_.moduleKey).mkString(", ") ++ ")"
+ def targetClasspath = ClassPath()
+ def exportedClasspath = ClassPath()
+ override def show: String = this.getClass.getSimpleName + "( " + dependencies.map(_.show).mkString(", ") + " )"
}
-case class Stage1Dependency(cbtHasChanged: Boolean, mavenCache: File, nailgunTarget: File, stage1Target: File, compatibilityTarget: File)(implicit val logger: Logger) extends DependencyImplementation{
- override def needsUpdate = false
+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(nailgunTarget, stage1Target) )
- val compatibilityDependency = CompatibilityDependency(cbtHasChanged, compatibilityTarget)
- override def dependencies = Seq(
- compatibilityDependency
- ) ++
- MavenResolver(cbtHasChanged,mavenCache,mavenCentral).bind(
- MavenDependency("org.scala-lang","scala-library",constants.scalaVersion),
- MavenDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,constants.scalaXmlVersion)
- )
+ override def exportedClasspath = ClassPath( Seq(target) )
+ override def dependencies = _dependencies
}
-case class CompatibilityDependency(cbtHasChanged: Boolean, compatibilityTarget: File)(implicit val logger: Logger) extends DependencyImplementation{
- override def needsUpdate = false
- override def targetClasspath = exportedClasspath
- override def exportedClasspath = ClassPath( Seq(compatibilityTarget) )
- override def dependencies = Seq()
-}
-case class CbtDependency(cbtHasChanged: Boolean, mavenCache: File, nailgunTarget: File, stage1Target: File, stage2Target: File, compatibilityTarget: File)(implicit val logger: Logger) extends DependencyImplementation{
- override def needsUpdate = false
- override def targetClasspath = exportedClasspath
- override def exportedClasspath = ClassPath( Seq( stage2Target ) )
- val stage1Dependency = Stage1Dependency(cbtHasChanged, mavenCache, nailgunTarget, stage1Target, compatibilityTarget)
- override def dependencies = Seq(
- stage1Dependency
- ) ++
- 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")
+ )
)
}
@@ -186,9 +184,11 @@ abstract class DependenciesProxy{
}
class BoundMavenDependencies(
- cbtHasChanged: Boolean, mavenCache: File, urls: Seq[URL], mavenDependencies: Seq[MavenDependency]
-)(implicit logger: Logger) extends Dependencies(
- mavenDependencies.map( BoundMavenDependency(cbtHasChanged,mavenCache,_,urls) )
+ cbtLastModified: Long, mavenCache: File, urls: Seq[URL], mavenDependencies: Seq[MavenDependency]
+)(
+ implicit logger: Logger, transientCache: java.util.Map[AnyRef,AnyRef]
+) extends Dependencies(
+ mavenDependencies.map( BoundMavenDependency(cbtLastModified,mavenCache,_,urls) )
)
case class MavenDependency(
groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none
@@ -202,8 +202,11 @@ object MavenDependency{
}
// FIXME: take MavenResolver instead of mavenCache and repositories separately
case class BoundMavenDependency(
- cbtHasChanged: Boolean, mavenCache: File, mavenDependency: MavenDependency, repositories: Seq[URL]
-)(implicit val logger: Logger) extends ArtifactInfo with DependencyImplementation{
+ cbtLastModified: Long, mavenCache: File, mavenDependency: MavenDependency, repositories: Seq[URL]
+)(
+ implicit val logger: Logger, val transientCache: java.util.Map[AnyRef,AnyRef]
+) extends ArtifactInfo with DependencyImplementation{
+ def moduleKey = this.getClass.getName ++ "(" ++ mavenDependency.serialize ++ ")"
val MavenDependency( groupId, artifactId, version, classifier ) = mavenDependency
assert(
Option(groupId).collect{
@@ -221,13 +224,13 @@ case class BoundMavenDependency(
version != "" && version != null && !version.startsWith(" ") && !version.endsWith(" "),
s"not a valid version: '$version'"
)
- override def show: String = this.getClass.getSimpleName ++ "(" ++ mavenDependency.serialize ++ ")"
+ 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 "")
-
+
//private def coursierJarFile = userHome++"/.coursier/cache/v1/https/repo1.maven.org/maven2"++basePath++".jar"
def exportedJars = Seq( jar )
@@ -253,17 +256,11 @@ case class BoundMavenDependency(
StandardCharsets.UTF_8
).mkString("\n").split(" ").head.trim
}
-
- private object jarSha1Cache extends Cache[String]
- def jarSha1: String = jarSha1Cache{ resolveHash("jar", true) }
-
- private object pomSha1Cache extends Cache[String]
- def pomSha1: String = pomSha1Cache{ resolveHash("pom", false) }
- private object jarCache extends Cache[File]
- def jar: File = jarCache{ resolve("jar", Some(jarSha1), true) }
- private object pomCache extends Cache[File]
- def pom: File = pomCache{ resolve("pom", Some(pomSha1), false) }
+ def jarSha1: String = taskCache[BoundMavenDependency]("jarSha1").memoize{ resolveHash("jar", true) }
+ def pomSha1: String = taskCache[BoundMavenDependency]("pomSha1").memoize{ resolveHash("pom", false) }
+ def jar: File = taskCache[BoundMavenDependency]("jar").memoize{ resolve("jar", Some(jarSha1), true) }
+ def pom: File = taskCache[BoundMavenDependency]("pom").memoize{ resolve("pom", Some(pomSha1), false) }
private def pomXml = XML.loadFile(pom.string)
// ========== pom traversal ==========
@@ -272,7 +269,7 @@ case class BoundMavenDependency(
(pomXml \ "parent").collect{
case parent =>
BoundMavenDependency(
- cbtHasChanged: Boolean,
+ cbtLastModified: Long,
mavenCache,
MavenDependency(
(parent \ "groupId").text,
@@ -280,7 +277,7 @@ case class BoundMavenDependency(
(parent \ "version").text
),
repositories
- )(logger)
+ )
}.flatMap(_.transitivePom) :+ this
}
@@ -310,7 +307,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" =>
@@ -335,7 +332,7 @@ case class BoundMavenDependency(
MavenDependency( groupId, artifactId, version, classifier )
}.toVector
}.map(
- BoundMavenDependency( cbtHasChanged, mavenCache, _, repositories )
+ BoundMavenDependency( cbtLastModified, mavenCache, _, repositories )
).to
}
}
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index efccfb4..750b0bc 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -9,6 +9,8 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
// will create new instances given the context, which means operations in the
// overrides will happen multiple times and if they are not idempotent stuff likely breaks
def context: Context
+ def moduleKey: String = "BaseBuild("+projectDirectory.string+")"
+ implicit def transientCache: java.util.Map[AnyRef,AnyRef] = context.transientCache
// library available to builds
implicit protected final val logger: Logger = context.logger
@@ -59,7 +61,7 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
// FIXME: this should probably be removed
Resolver( mavenCentral ).bind(
"org.scala-lang" % "scala-library" % scalaVersion
- )
+ ) ++ ( if(localJars.nonEmpty) Seq( BinaryDependency(localJars, Nil) ) else Nil )
// ========== paths ==========
final private val defaultSourceDirectory = projectDirectory ++ "/src"
@@ -99,7 +101,7 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
}
logEmptySourceDirectories()
- def Resolver( urls: URL* ) = MavenResolver( context.cbtHasChanged, context.paths.mavenCache, urls: _* )
+ def Resolver( urls: URL* ) = MavenResolver( context.cbtLastModified, context.paths.mavenCache, urls: _* )
def ScalaDependency(
groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none,
@@ -118,17 +120,16 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
.flatMap(_.listFiles)
.filter(_.toString.endsWith(".jar"))
- override def dependencyClasspath : ClassPath = ClassPath(localJars) ++ super.dependencyClasspath
+ override def dependencyClasspath : ClassPath = super.dependencyClasspath
- protected def compileDependencies: Seq[Dependency] = Nil
- final def compileClasspath : ClassPath =
- dependencyClasspath ++ ClassPath( compileDependencies.flatMap(_.exportedClasspath.files).distinct )
+ protected def compileDependencies: Seq[Dependency] = dependencies
+ final def compileClasspath : ClassPath = Dependencies(compileDependencies).classpath
def resourceClasspath: ClassPath = {
val resourcesDirectory = projectDirectory ++ "/resources"
ClassPath( if(resourcesDirectory.exists) Seq(resourcesDirectory) else Nil )
}
- def exportedClasspath : ClassPath = ClassPath(compile.toSeq) ++ resourceClasspath
+ def exportedClasspath : ClassPath = ClassPath(compileFile.toSeq) ++ resourceClasspath
def targetClasspath = ClassPath(Seq(compileTarget))
// ========== compile, run, test ==========
@@ -139,26 +140,20 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
"-unchecked"
)
- private object needsUpdateCache extends Cache[Boolean]
- def needsUpdate: Boolean = needsUpdateCache(
- context.cbtHasChanged
- || lib.needsUpdate( sourceFiles, compileStatusFile )
- || transitiveDependencies.filterNot(_ == context.parentBuild).exists(_.needsUpdate)
- )
+ final def lastModified: Long = compile.getOrElse(0L)
- private object compileCache extends Cache[Option[File]]
- def compile: Option[File] = compileCache{
+ final def compileFile: Option[File] = compile.map(_ => compileTarget)
+
+ def compile: Option[Long] = taskCache[BaseBuild]("_compile").memoize{
lib.compile(
- context.cbtHasChanged,
- needsUpdate || context.parentBuild.map(_.needsUpdate).getOrElse(false),
- sourceFiles, compileTarget, compileStatusFile, compileClasspath,
+ context.cbtLastModified,
+ sourceFiles, compileTarget, compileStatusFile, compileDependencies,
context.paths.mavenCache, scalacOptions, context.classLoaderCache,
zincVersion = zincVersion, scalaVersion = scalaVersion
)
}
-
- def mainClasses: Seq[Class[_]] = compile.toSeq.flatMap( lib.mainClasses( _, classLoader(classLoaderCache) ) )
+ def mainClasses: Seq[Class[_]] = compileFile.toSeq.flatMap( lib.mainClasses( _, classLoader(classLoaderCache) ) )
def runClass: Option[String] = lib.runClass( mainClasses ).map( _.getName )
@@ -186,7 +181,7 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
System.setProperty(colorized, "true")
}
- val scalac = new ScalaCompilerDependency(context.cbtHasChanged, context.paths.mavenCache, scalaVersion)
+ val scalac = new ScalaCompilerDependency(context.cbtLastModified, context.paths.mavenCache, scalaVersion)
lib.runMain(
"scala.tools.nsc.MainGenericRunner",
Seq(
@@ -268,24 +263,6 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
def finalBuild: BuildInterface = this
override def show = this.getClass.getSimpleName ++ "(" ++ projectDirectory.string ++ ")"
- // TODO: allow people not provide the method name, maybe via macro
- // TODO: pull this out into lib
- /**
- caches given value in context keyed with given key and projectDirectory
- the context is fresh on every complete run of cbt
- */
- def cached[T <: AnyRef](name: String)(task: => T): T = {
- val cache = context.taskCache
- val key = (projectDirectory,name)
- if( cache.containsKey(key) ){
- cache.get(key).asInstanceOf[T]
- } else{
- val value = task
- cache.put( key, value )
- value
- }
- }
-
// a method that can be called only to trigger any side-effects
final def `void` = ()
}
diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala
index cf515bb..1b05214 100644
--- a/stage2/BuildBuild.scala
+++ b/stage2/BuildBuild.scala
@@ -32,8 +32,7 @@ trait BuildBuildWithoutEssentials extends BaseBuild{
override def dependencies =
super.dependencies :+ context.cbtDependency
def managedBuildDirectory: java.io.File = lib.realpath( projectDirectory.parent )
- private object managedBuildCache extends Cache[BuildInterface]
- def managedBuild = managedBuildCache{
+ def managedBuild = taskCache[BuildBuildWithoutEssentials]("managedBuild").memoize{
val managedBuildFile = projectDirectory++"/build.scala"
logger.composition("Loading build at " ++ managedBuildDirectory.toString)
val build = (
diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala
index 197a7a1..236f958 100644
--- a/stage2/BuildDependency.scala
+++ b/stage2/BuildDependency.scala
@@ -16,15 +16,18 @@ trait TriggerLoop extends DependencyImplementation{
}
/** You likely want to use the factory method in the BasicBuild class instead of this. */
final case class DirectoryDependency(context: Context) extends TriggerLoop{
+ override def toString = show
override def show = this.getClass.getSimpleName ++ "(" ++ context.projectDirectory.string ++ ")"
+ def moduleKey = this.getClass.getName ++ "("+context.projectDirectory.string+")"
lazy val logger = context.logger
override lazy val lib: Lib = new Lib(logger)
+ def transientCache = context.transientCache
private lazy val root = lib.loadRoot( context.copy(args=Seq()) )
lazy val build = root.finalBuild
def exportedClasspath = ClassPath()
def dependencies = Seq(build)
def triggerLoopFiles = root.triggerLoopFiles
- def needsUpdate = build.needsUpdate
+ def lastModified = build.lastModified
def targetClasspath = ClassPath()
}
/*
diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala
index 650fd09..059d650 100644
--- a/stage2/GitDependency.scala
+++ b/stage2/GitDependency.scala
@@ -15,15 +15,14 @@ case class GitDependency(
)(implicit val logger: Logger, classLoaderCache: ClassLoaderCache, context: Context ) extends DependencyImplementation{
import GitDependency._
override def lib = new Lib(logger)
-
+ def moduleKey = this.getClass.getName ++ "(" ++ url ++ subDirectory.map("/" ++ _).getOrElse("") ++ "#" ++ ref ++ ")"
+ def transientCache = context.transientCache
// TODO: add support for authentication via ssh and/or https
// See http://www.codeaffine.com/2014/12/09/jgit-authentication/
private val GitUrl( _, domain, path ) = url
private val credentialsFile = context.projectDirectory ++ "/git.login"
- private object checkoutCache extends Cache[File]
-
private def authenticate(_git: CloneCommand) =
if(!credentialsFile.exists){
_git
@@ -36,7 +35,7 @@ case class GitDependency(
_git.setCredentialsProvider( new UsernamePasswordCredentialsProvider(user, password) )
}
- def checkout: File = checkoutCache{
+ def checkout: File = taskCache[GitDependency]("checkout").memoize{
val checkoutDirectory = context.cache ++ s"/git/$domain/$path/$ref"
val _git = if(checkoutDirectory.exists){
logger.git(s"Found existing checkout of $url#$ref in $checkoutDirectory")
@@ -65,8 +64,7 @@ case class GitDependency(
assert( actualRef == ref, s"actual ref '$actualRef' does not match expected ref '$ref'")
checkoutDirectory
}
- private object dependencyCache extends Cache[DependencyImplementation]
- def dependency = dependencyCache{
+ def dependency = taskCache[GitDependency]("dependency").memoize{
DirectoryDependency(
context.copy(
projectDirectory = checkout ++ subDirectory.map("/" ++ _).getOrElse("")
@@ -78,5 +76,5 @@ case class GitDependency(
def exportedClasspath = ClassPath()
private[cbt] def targetClasspath = exportedClasspath
- def needsUpdate: Boolean = false
+ def lastModified: Long = dependency.lastModified
}
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index ca85555..45803b4 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -15,7 +15,7 @@ import scala.util._
case class Developer(id: String, name: String, timezone: String, url: URL)
/** Don't extend. Create your own libs :). */
-final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
+final class Lib(val logger: Logger) extends Stage1Lib(logger) with Scaffold{
lib =>
val buildClassName = "Build"
@@ -74,7 +74,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
}
def docJar(
- cbtHasChanged: Boolean,
+ cbtLastModified: Long,
scalaVersion: String,
sourceFiles: Seq[File],
dependencyClasspath: ClassPath,
@@ -86,7 +86,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
compileArgs: Seq[String],
classLoaderCache: ClassLoaderCache,
mavenCache: File
- ): Option[File] = {
+ )(implicit transientCache: java.util.Map[AnyRef,AnyRef]): Option[File] = {
if(sourceFiles.isEmpty){
None
} else {
@@ -101,7 +101,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
runMain(
"scala.tools.nsc.ScalaDoc",
args,
- ScalaDependencies(cbtHasChanged,mavenCache,scalaVersion)(logger).classLoader(classLoaderCache)
+ new ScalaDependencies(cbtLastModified,mavenCache,scalaVersion).classLoader(classLoaderCache)
)
}
lib.jarFile(
diff --git a/stage2/PackageJars.scala b/stage2/PackageJars.scala
index ff89284..7079786 100644
--- a/stage2/PackageJars.scala
+++ b/stage2/PackageJars.scala
@@ -10,20 +10,17 @@ trait PackageJars extends BaseBuild with ArtifactInfo{
Seq(() => jar, () => docJar, () => srcJar)
)( _() ).flatten
- private object cacheJarBasicBuild extends Cache[Option[File]]
- def jar: Option[File] = cacheJarBasicBuild{
- compile.flatMap( lib.jar( artifactId, scalaMajorVersion, version, _, jarTarget ) )
+ def jar: Option[File] = taskCache[PackageJars]("jar").memoize{
+ compileFile.flatMap( lib.jar( artifactId, scalaMajorVersion, version, _, jarTarget ) )
}
- private object cacheSrcJarBasicBuild extends Cache[Option[File]]
- def srcJar: Option[File] = cacheSrcJarBasicBuild{
+ def srcJar: Option[File] = taskCache[PackageJars]("srcJar").memoize{
lib.srcJar( sourceFiles, artifactId, scalaMajorVersion, version, scalaTarget )
}
- private object cacheDocBasicBuild extends Cache[Option[File]]
- def docJar: Option[File] = cacheDocBasicBuild{
+ def docJar: Option[File] = taskCache[PackageJars]("docJar").memoize{
lib.docJar(
- context.cbtHasChanged,
+ context.cbtLastModified,
scalaVersion, sourceFiles, compileClasspath, docTarget,
jarTarget, artifactId, scalaMajorVersion, version,
scalacOptions, context.classLoaderCache, context.paths.mavenCache
diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala
index dfbead3..0f5b557 100644
--- a/stage2/Stage2.scala
+++ b/stage2/Stage2.scala
@@ -1,12 +1,13 @@
package cbt
import java.io._
+import java.util._
object Stage2 extends Stage2Base{
def getBuild(context: Context) = {
new Lib( context.logger ).loadRoot( context ).finalBuild
}
- def run( args: Stage2Args ): Unit = {
+ def run( args: Stage2Args ): ExitCode = {
import args.logger
val paths = CbtPaths(args.cbtHome,args.cache)
import paths._
@@ -22,18 +23,17 @@ object Stage2 extends Stage2Base{
0
}
val task = args.args.lift( taskIndex )
-
- val context: Context = ContextImplementation(
+
+ val context: Context = new ContextImplementation(
args.cwd,
args.cwd,
args.args.drop( taskIndex +1 ).toArray,
logger.enabledLoggers.toArray,
logger.start,
- args.cbtHasChanged,
+ args.stage2LastModified,
null,
- args.permanentKeys,
- args.permanentClassLoaders,
- new java.util.concurrent.ConcurrentHashMap,
+ args.classLoaderCache.hashMap,
+ args.transientCache,
args.cache,
args.cbtHome,
args.cbtHome,
@@ -75,10 +75,13 @@ object Stage2 extends Stage2Base{
logger.loop(s"Re-running $task for " ++ build.show)
call(build)
}
+ ExitCode.Success
} else {
val code = call(build)
logger.stage2(s"Stage2 end")
- System.exit(code.integer)
+ code
}
+
+ res
}
}
diff --git a/stage2/ToolsStage2.scala b/stage2/ToolsStage2.scala
index df615fc..2899173 100644
--- a/stage2/ToolsStage2.scala
+++ b/stage2/ToolsStage2.scala
@@ -1,12 +1,13 @@
package cbt
import java.io._
object ToolsStage2 extends Stage2Base{
- def run( _args: Stage2Args ): Unit = {
+ def run( _args: Stage2Args ): ExitCode = {
val args = _args.args.dropWhile(Seq("tools","direct") contains _)
val lib = new Lib(_args.logger)
- val toolsTasks = new ToolsTasks(lib, args, _args.cwd, _args.classLoaderCache, _args.cache, _args.cbtHome, _args.cbtHasChanged)
+ val toolsTasks = new ToolsTasks(lib, args, _args.cwd, _args.classLoaderCache, _args.cache, _args.cbtHome, _args.stage2LastModified)
new lib.ReflectObject(toolsTasks){
def usage: String = "Available methods: " ++ lib.taskNames(toolsTasks.getClass).mkString(" ")
}.callNullary(args.lift(0))
+ ExitCode.Success
}
}
diff --git a/stage2/ToolsTasks.scala b/stage2/ToolsTasks.scala
index 975b49a..6acf72c 100644
--- a/stage2/ToolsTasks.scala
+++ b/stage2/ToolsTasks.scala
@@ -9,12 +9,13 @@ class ToolsTasks(
classLoaderCache: ClassLoaderCache,
cache: File,
cbtHome: File,
- cbtHasChanged: Boolean
+ cbtLastModified: Long
){
private val paths = CbtPaths(cbtHome, cache)
import paths._
- private def Resolver( urls: URL* ) = MavenResolver(cbtHasChanged,mavenCache,urls: _*)
+ private def Resolver( urls: URL* ) = MavenResolver(cbtLastModified,mavenCache,urls: _*)
implicit val logger: Logger = lib.logger
+ implicit val transientCache: java.util.Map[AnyRef,AnyRef] = new java.util.HashMap
def createMain: Unit = lib.createMain( cwd )
def createBuild: Unit = lib.createBuild( cwd )
def gui = NailgunLauncher.main(Array(
@@ -55,7 +56,7 @@ class ToolsTasks(
}
def scala = {
val version = args.lift(1).getOrElse(constants.scalaVersion)
- val scalac = new ScalaCompilerDependency( cbtHasChanged, mavenCache, version )
+ val scalac = new ScalaCompilerDependency( cbtLastModified, mavenCache, version )
val _args = Seq("-cp", scalac.classpath.string) ++ args.drop(2)
lib.runMain(
"scala.tools.nsc.MainGenericRunner", _args, scalac.classLoader(classLoaderCache)
@@ -96,14 +97,15 @@ class ToolsTasks(
val n = valName(d)
s"""
// ${d.groupId}:${d.artifactId}:${d.version}
- download(new URL(mavenUrl + "${d.basePath(true)}.jar"), Paths.get(${n}File), "${d.jarSha1}");
-
String[] ${n}ClasspathArray = new String[]{${deps.sortBy(_.jar).map(valName(_)+"File").mkString(", ")}};
- String ${n}Classpath = classpath( ${n}ClasspathArray );
- ClassLoader $n =
- classLoaderCache.contains( ${n}Classpath )
- ? classLoaderCache.get( ${n}Classpath )
- : classLoaderCache.put( classLoader( ${n}File, $parentString ), ${n}Classpath );"""
+ ClassLoader $n = loadDependency(
+ mavenUrl + "${d.basePath(true)}.jar",
+ ${n}File,
+ "${d.jarSha1}",
+ classLoaderCache,
+ $parentString,
+ ${n}ClasspathArray
+ );"""
}
}
val assignments = codeEach(zinc) ++ codeEach(scalaXml)
@@ -115,6 +117,7 @@ import java.io.*;
import java.nio.file.*;
import java.net.*;
import java.security.*;
+import java.util.*;
import static cbt.Stage0Lib.*;
import static cbt.NailgunLauncher.*;
@@ -129,13 +132,13 @@ class EarlyDependencies{
${files.map(d => s""" String ${valName(d)}File;""").mkString("\n")}
public EarlyDependencies(
- String mavenCache, String mavenUrl, ClassLoaderCache2<ClassLoader> classLoaderCache, ClassLoader rootClassLoader
- ) throws Exception {
+ String mavenCache, String mavenUrl, ClassLoaderCache classLoaderCache, ClassLoader rootClassLoader
+ ) throws Throwable {
${files.map(d => s""" ${valName(d)}File = mavenCache + "${d.basePath(true)}.jar";""").mkString("\n")}
${scalaDeps.map(d => s""" download(new URL(mavenUrl + "${d.basePath(true)}.jar"), Paths.get(${valName(d)}File), "${d.jarSha1}");""").mkString("\n")}
${assignments.mkString("\n")}
-
+
classLoader = scalaXml_${scalaXmlVersion.replace(".","_")}_;
classpathArray = scalaXml_${scalaXmlVersion.replace(".","_")}_ClasspathArray;
diff --git a/stage2/plugins/Dotty.scala b/stage2/plugins/Dotty.scala
index 8671fb6..6fe5dd3 100644
--- a/stage2/plugins/Dotty.scala
+++ b/stage2/plugins/Dotty.scala
@@ -8,18 +8,15 @@ trait Dotty extends BaseBuild{
def dottyVersion: String = "0.1-20160926-ec28ea1-NIGHTLY"
def dottyOptions: Seq[String] = Seq()
override def scalaTarget: File = target ++ s"/dotty-$dottyVersion"
-
+
private lazy val dottyLib = new DottyLib(
- logger, context.cbtHasChanged, context.paths.mavenCache,
+ logger, context.cbtLastModified, context.paths.mavenCache,
context.classLoaderCache, dottyVersion = dottyVersion
)
- private object compileCache extends Cache[Option[File]]
- override def compile: Option[File] = compileCache{
+ override def compile: Option[Long] = taskCache[Dotty]("compile").memoize{
dottyLib.compile(
- needsUpdate || context.parentBuild.map(_.needsUpdate).getOrElse(false),
- sourceFiles, compileTarget, compileStatusFile, compileClasspath,
- dottyOptions
+ sourceFiles, compileTarget, compileStatusFile, compileDependencies, dottyOptions
)
}
@@ -37,15 +34,15 @@ trait Dotty extends BaseBuild{
class DottyLib(
logger: Logger,
- cbtHasChanged: Boolean,
+ cbtLastModified: Long,
mavenCache: File,
classLoaderCache: ClassLoaderCache,
dottyVersion: String
-){
+)(implicit transientCache: java.util.Map[AnyRef,AnyRef]){
val lib = new Lib(logger)
import lib._
- private def Resolver(urls: URL*) = MavenResolver(cbtHasChanged, mavenCache, urls: _*)
+ private def Resolver(urls: URL*) = MavenResolver(cbtLastModified, mavenCache, urls: _*)
private lazy val dottyDependency = Resolver(mavenCentral).bindOne(
MavenDependency("ch.epfl.lamp","dotty_2.11",dottyVersion)
)
@@ -95,22 +92,24 @@ class DottyLib(
}
def compile(
- needsRecompile: Boolean,
- files: Seq[File],
+ sourceFiles: Seq[File],
compileTarget: File,
statusFile: File,
- classpath: ClassPath,
+ dependencies: Seq[Dependency],
dottyOptions: Seq[String]
- ): Option[File] = {
-
+ ): 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 ){
- val start = System.currentTimeMillis
+ val start = System.currentTimeMillis
+ val lastCompiled = statusFile.lastModified
+ if( d.lastModified > lastCompiled || sourceFiles.exists(_.lastModified > lastCompiled) ){
val _class = "dotty.tools.dotc.Main"
val dualArgs =
@@ -119,7 +118,7 @@ class DottyLib(
)
val singleArgs = dottyOptions.map( "-S" ++ _ )
- val code =
+ val code =
try{
System.err.println("Compiling with Dotty to " ++ compileTarget.toString)
compileTarget.mkdirs
@@ -129,7 +128,7 @@ class DottyLib(
dualArgs ++ singleArgs ++ Seq(
"-bootclasspath", dottyDependency.classpath.string, // let's put cp last. It so long
"-classpath", classpath.string // let's put cp last. It so long
- ) ++ files.map(_.toString),
+ ) ++ sourceFiles.map(_.toString),
dottyDependency.classLoader(classLoaderCache)
)
}
@@ -151,7 +150,7 @@ ${dottyDependency.classpath.strings.mkString(":\\\n")} \\
-classpath \\
${classpath.strings.mkString(":\\\n")} \\
\\
-${files.sorted.mkString(" \\\n")}
+${sourceFiles.sorted.mkString(" \\\n")}
"""
)
ExitCode.Failure
@@ -165,8 +164,10 @@ ${files.sorted.mkString(" \\\n")}
} 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 )
}
}
}
diff --git a/test/library-test/build/build.scala b/test/library-test/build/build.scala
index 85c09ac..f16f0c2 100644
--- a/test/library-test/build/build.scala
+++ b/test/library-test/build/build.scala
@@ -1,6 +1,6 @@
import cbt._
-// cbt:https://github.com/cvogt/cbt.git#fe04889a6c3fe73ccdb4b19b44ac62e2b1a96f7d
+// cbt:https://github.com/cvogt/cbt.git#f11b8318b85f16843d8cfa0743f64c1576614ad6
class Build(val context: Context) extends BaseBuild with PackageJars{
def groupId = "cbt.test"
def version = "0.1"
diff --git a/test/simple-fixed-cbt/build/build.scala b/test/simple-fixed-cbt/build/build.scala
index 857f6b7..2ce1d02 100644
--- a/test/simple-fixed-cbt/build/build.scala
+++ b/test/simple-fixed-cbt/build/build.scala
@@ -1,6 +1,6 @@
import cbt._
-// cbt:https://github.com/cvogt/cbt.git#fe04889a6c3fe73ccdb4b19b44ac62e2b1a96f7d
+// cbt:https://github.com/cvogt/cbt.git#f11b8318b85f16843d8cfa0743f64c1576614ad6
class Build(val context: cbt.Context) extends PackageJars{
override def dependencies = super.dependencies ++ Seq(
DirectoryDependency( context.cbtHome ++ "/test/library-test" )
diff --git a/test/simple-fixed/build/build.scala b/test/simple-fixed/build/build.scala
index a0b192a..b46c337 100644
--- a/test/simple-fixed/build/build.scala
+++ b/test/simple-fixed/build/build.scala
@@ -5,7 +5,7 @@ class Build(context: cbt.Context) extends BasicBuild(context){
super.dependencies
++
Seq(
- GitDependency("https://github.com/cvogt/cbt.git", "fe04889a6c3fe73ccdb4b19b44ac62e2b1a96f7d", Some("test/library-test"))
+ GitDependency("https://github.com/cvogt/cbt.git", "f11b8318b85f16843d8cfa0743f64c1576614ad6", Some("test/library-test"))
)
++
Resolver(mavenCentral).bind(
diff --git a/test/simple/build/build.scala b/test/simple/build/build.scala
index 3d0e105..a132dd1 100644
--- a/test/simple/build/build.scala
+++ b/test/simple/build/build.scala
@@ -5,7 +5,7 @@ class Build(val context: cbt.Context) extends BaseBuild{
super.dependencies
++
Seq(
- GitDependency("https://github.com/cvogt/cbt.git", "fe04889a6c3fe73ccdb4b19b44ac62e2b1a96f7d", Some("test/library-test"))
+ GitDependency("https://github.com/cvogt/cbt.git", "f11b8318b85f16843d8cfa0743f64c1576614ad6", Some("test/library-test"))
) ++
// FIXME: make the below less verbose
Resolver( mavenCentral ).bind(
diff --git a/test/test.scala b/test/test.scala
index 0eb0bef..e9da8bf 100644
--- a/test/test.scala
+++ b/test/test.scala
@@ -1,9 +1,9 @@
package cbt
package test
-import java.util.concurrent.ConcurrentHashMap
import java.io.File
import java.nio.file._
import java.net.URL
+import java.util.{Iterator=>_,_}
import scala.concurrent._
import scala.concurrent.duration._
// micro framework
@@ -103,21 +103,21 @@ object Main{
val cache = cbtHome ++ "/cache"
val mavenCache = cache ++ "/maven"
- val cbtHasChanged = true
- def Resolver(urls: URL*) = MavenResolver(cbtHasChanged, mavenCache, urls: _*)
+ val cbtLastModified = System.currentTimeMillis
+ implicit val transientCache: java.util.Map[AnyRef,AnyRef] = new java.util.HashMap
+ def Resolver(urls: URL*) = MavenResolver(cbtLastModified, mavenCache, urls: _*)
{
- val noContext = ContextImplementation(
+ val noContext = new ContextImplementation(
cbtHome ++ "/test/nothing",
cbtHome,
Array(),
Array(),
start,
- cbtHasChanged,
+ cbtLastModified,
null,
- new ConcurrentHashMap[String,AnyRef],
- new ConcurrentHashMap[AnyRef,ClassLoader],
- new java.util.concurrent.ConcurrentHashMap[AnyRef,AnyRef],
+ new HashMap[AnyRef,AnyRef],
+ new HashMap[AnyRef,AnyRef],
cache,
cbtHome,
cbtHome,