aboutsummaryrefslogtreecommitdiff
path: root/nailgun_launcher
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-02-09 21:20:11 -0500
committerChristopher Vogt <oss.nsp@cvogt.org>2017-02-09 22:43:00 -0500
commite8673866b79f7473391dcee26243eee80d5d3cb6 (patch)
tree16146affeebdb58cd302a1f8527220c906818c96 /nailgun_launcher
parentbee13ba7a4458482ce00a5c6bae4cd64328c4e5e (diff)
downloadcbt-e8673866b79f7473391dcee26243eee80d5d3cb6.tar.gz
cbt-e8673866b79f7473391dcee26243eee80d5d3cb6.tar.bz2
cbt-e8673866b79f7473391dcee26243eee80d5d3cb6.zip
idempotent change propagation
using lastModified instead of a non-idempotent needsUpdate flag this fixes a bug where dependees would not be rebuilt if cbt exited or was killed after dependencies were already rebuilt.
Diffstat (limited to 'nailgun_launcher')
-rw-r--r--nailgun_launcher/BuildStage1Result.java8
-rw-r--r--nailgun_launcher/CbtURLClassLoader.java8
-rw-r--r--nailgun_launcher/ClassLoaderCache.java66
-rw-r--r--nailgun_launcher/EarlyDependencies.java140
-rw-r--r--nailgun_launcher/JavaCache.java40
-rw-r--r--nailgun_launcher/NailgunLauncher.java80
-rw-r--r--nailgun_launcher/Stage0Lib.java53
7 files changed, 245 insertions, 150 deletions
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 43d07f4..fac7050 100644
--- a/nailgun_launcher/CbtURLClassLoader.java
+++ b/nailgun_launcher/CbtURLClassLoader.java
@@ -15,7 +15,7 @@ public class CbtURLClassLoader extends java.net.URLClassLoader{
+ "\n)"
);
}
- JavaCache<Class> cache = new JavaCache<Class>( new ConcurrentHashMap<Object,Object>() );
+ 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);
@@ -24,11 +24,11 @@ public class CbtURLClassLoader extends java.net.URLClassLoader{
public Class loadClass(String name, Boolean resolve) throws ClassNotFoundException{
//System.out.println("loadClass("+name+") on \n"+this);
synchronized( cache ){
- if(!cache.contains(name))
+ if(!cache.containsKey(name))
try{
- cache.put(super.loadClass(name, resolve), name);
+ cache.put(name, super.loadClass(name, resolve));
} catch (ClassNotFoundException e){
- cache.put(Object.class, name);
+ cache.put(name, Object.class);
}
Class _class = cache.get(name);
if(_class == Object.class){
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/EarlyDependencies.java b/nailgun_launcher/EarlyDependencies.java
index fdb54b5..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.*;
@@ -28,7 +29,7 @@ class EarlyDependencies{
String scalaLibrary_2_10_6_File;
public EarlyDependencies(
- String mavenCache, String mavenUrl, JavaCache<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";
@@ -46,95 +47,104 @@ class EarlyDependencies{
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.6
- download(new URL(mavenUrl + "/org/scala-lang/scala-library/2.10.6/scala-library-2.10.6.jar"), Paths.get(scalaLibrary_2_10_6_File), "421989aa8f95a05a4f894630aad96b8c7b828732");
-
String[] scalaLibrary_2_10_6_ClasspathArray = new String[]{scalaLibrary_2_10_6_File};
- String scalaLibrary_2_10_6_Classpath = classpath( scalaLibrary_2_10_6_ClasspathArray );
- ClassLoader scalaLibrary_2_10_6_ =
- classLoaderCache.contains( scalaLibrary_2_10_6_Classpath )
- ? classLoaderCache.get( scalaLibrary_2_10_6_Classpath )
- : classLoaderCache.put( classLoader( scalaLibrary_2_10_6_File, rootClassLoader ), scalaLibrary_2_10_6_Classpath );
+ 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
- download(new URL(mavenUrl + "/org/scala-lang/scala-reflect/2.10.6/scala-reflect-2.10.6.jar"), Paths.get(scalaReflect_2_10_6_File), "3259f3df0f166f017ef5b2d385445808398c316c");
-
String[] scalaReflect_2_10_6_ClasspathArray = new String[]{scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
- String scalaReflect_2_10_6_Classpath = classpath( scalaReflect_2_10_6_ClasspathArray );
- ClassLoader scalaReflect_2_10_6_ =
- classLoaderCache.contains( scalaReflect_2_10_6_Classpath )
- ? classLoaderCache.get( scalaReflect_2_10_6_Classpath )
- : classLoaderCache.put( classLoader( scalaReflect_2_10_6_File, scalaLibrary_2_10_6_ ), scalaReflect_2_10_6_Classpath );
+ 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
- download(new URL(mavenUrl + "/com/typesafe/sbt/sbt-interface/0.13.12/sbt-interface-0.13.12.jar"), Paths.get(sbtInterface_0_13_12_File), "fcc7875c02f0d4641fac0518121bd71475d3909b");
-
String[] sbtInterface_0_13_12_ClasspathArray = new String[]{sbtInterface_0_13_12_File, scalaLibrary_2_10_6_File, scalaReflect_2_10_6_File};
- String sbtInterface_0_13_12_Classpath = classpath( sbtInterface_0_13_12_ClasspathArray );
- ClassLoader sbtInterface_0_13_12_ =
- classLoaderCache.contains( sbtInterface_0_13_12_Classpath )
- ? classLoaderCache.get( sbtInterface_0_13_12_Classpath )
- : classLoaderCache.put( classLoader( sbtInterface_0_13_12_File, scalaReflect_2_10_6_ ), sbtInterface_0_13_12_Classpath );
+ 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
- download(new URL(mavenUrl + "/org/scala-lang/scala-compiler/2.10.6/scala-compiler-2.10.6.jar"), Paths.get(scalaCompiler_2_10_6_File), "9b15174852f5b6bb1edbf303d5722286a0a54011");
-
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};
- String scalaCompiler_2_10_6_Classpath = classpath( scalaCompiler_2_10_6_ClasspathArray );
- ClassLoader scalaCompiler_2_10_6_ =
- classLoaderCache.contains( scalaCompiler_2_10_6_Classpath )
- ? classLoaderCache.get( scalaCompiler_2_10_6_Classpath )
- : classLoaderCache.put( classLoader( scalaCompiler_2_10_6_File, sbtInterface_0_13_12_ ), scalaCompiler_2_10_6_Classpath );
+ 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
- download(new URL(mavenUrl + "/com/typesafe/sbt/compiler-interface/0.13.12/compiler-interface-0.13.12-sources.jar"), Paths.get(compilerInterface_0_13_12_File), "d9c3270576e162bf017b146af262364c2db87a32");
-
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};
- String compilerInterface_0_13_12_Classpath = classpath( compilerInterface_0_13_12_ClasspathArray );
- ClassLoader compilerInterface_0_13_12_ =
- classLoaderCache.contains( compilerInterface_0_13_12_Classpath )
- ? classLoaderCache.get( compilerInterface_0_13_12_Classpath )
- : classLoaderCache.put( classLoader( compilerInterface_0_13_12_File, scalaCompiler_2_10_6_ ), compilerInterface_0_13_12_Classpath );
+ 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
- download(new URL(mavenUrl + "/com/typesafe/sbt/incremental-compiler/0.13.12/incremental-compiler-0.13.12.jar"), Paths.get(incrementalCompiler_0_13_12_File), "259f6d24a5a3791bb233787d6a8e639c4ab86fe5");
-
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};
- String incrementalCompiler_0_13_12_Classpath = classpath( incrementalCompiler_0_13_12_ClasspathArray );
- ClassLoader incrementalCompiler_0_13_12_ =
- classLoaderCache.contains( incrementalCompiler_0_13_12_Classpath )
- ? classLoaderCache.get( incrementalCompiler_0_13_12_Classpath )
- : classLoaderCache.put( classLoader( incrementalCompiler_0_13_12_File, compilerInterface_0_13_12_ ), incrementalCompiler_0_13_12_Classpath );
+ 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
- download(new URL(mavenUrl + "/com/typesafe/zinc/zinc/0.3.12/zinc-0.3.12.jar"), Paths.get(zinc_0_3_12_File), "c4339e93f5b7273f49ad026248f4fdb1d4d6c7c4");
-
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};
- String zinc_0_3_12_Classpath = classpath( zinc_0_3_12_ClasspathArray );
- ClassLoader zinc_0_3_12_ =
- classLoaderCache.contains( zinc_0_3_12_Classpath )
- ? classLoaderCache.get( zinc_0_3_12_Classpath )
- : classLoaderCache.put( classLoader( zinc_0_3_12_File, incrementalCompiler_0_13_12_ ), zinc_0_3_12_Classpath );
+ 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;
diff --git a/nailgun_launcher/JavaCache.java b/nailgun_launcher/JavaCache.java
deleted file mode 100644
index 3ba12ab..0000000
--- a/nailgun_launcher/JavaCache.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package cbt;
-
-import java.util.*;
-import static java.io.File.pathSeparator;
-import static cbt.Stage0Lib.*;
-
-final class JavaCache<T>{
- Map<Object,Object> hashMap;
-
- public JavaCache(
- Map<Object,Object> hashMap
- ){
- this.hashMap = hashMap;
- }
-
- public T get( Object key ){
- @SuppressWarnings("unchecked")
- T t = (T) hashMap.get(
- hashMap.get( key )
- );
- return t;
- }
-
- public Boolean contains( Object key/*, Long timestamp*/ ){
- return hashMap.containsKey( key );/* && (
- (Long) hashMap.get( hashMap.get( hashMap.get(key) ) ) >= timestamp
- );*/
- }
-
- public T put( Object value, Object key/*, Long timestamp*/ ){
- LockableJavaKey keyObject = new LockableJavaKey();
- hashMap.put( key, keyObject );
- hashMap.put( keyObject, value );
- //hashMap.put( value, timestamp );
- @SuppressWarnings("unchecked")
- T t = (T) value;
- return t;
- }
-}
-class LockableJavaKey{}
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java
index c397810..8a330d8 100644
--- a/nailgun_launcher/NailgunLauncher.java
+++ b/nailgun_launcher/NailgunLauncher.java
@@ -26,12 +26,12 @@ 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 JavaCache<ClassLoader>(
+ new ClassLoaderCache(
(HashMap) get(context, "persistentCache")
)
);
@@ -43,6 +43,8 @@ public class NailgunLauncher{
.invoke(null, context, res);
}
+ public static long nailgunLauncherLastModified = -1; // this initial value should be overwritten, never read
+
public static void main( String[] args ) throws Throwable {
Long _start = System.currentTimeMillis();
if(args[0].equals("check-alive")){
@@ -76,13 +78,18 @@ 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
- // replace before returning, see below
- JavaCache<ClassLoader> classLoaderCache = new JavaCache<ClassLoader>(
+ // 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{
@@ -90,9 +97,9 @@ public class NailgunLauncher{
.classLoader
.loadClass("cbt.Stage1")
.getMethod(
- "run", String[].class, File.class, File.class, BuildStage1Result.class, Long.class, Map.class
+ "run", String[].class, File.class, File.class, BuildStage1Result.class, Map.class
).invoke(
- null, (Object) args, new File(cache), new File(CBT_HOME), res, start, classLoaderCache.hashMap
+ null, (Object) args, new File(cache), new File(CBT_HOME), res, classLoaderCache.hashMap
);
System.exit( exitCode );
@@ -115,7 +122,8 @@ public class NailgunLauncher{
}
public static BuildStage1Result buildStage1(
- Boolean changed, long start, String cache, String cbtHome, String compatibilityTarget, JavaCache<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,9 +136,11 @@ 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() ){
@@ -138,20 +148,20 @@ public class NailgunLauncher{
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 +174,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 +200,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 865b1cb..425ced3 100644
--- a/nailgun_launcher/Stage0Lib.java
+++ b/nailgun_launcher/Stage0Lib.java
@@ -11,6 +11,7 @@ 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){
@@ -47,25 +48,57 @@ 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[]{
@@ -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{