diff options
author | Jan Christopher Vogt <oss.nsp@cvogt.org> | 2017-02-10 01:11:22 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-10 01:11:22 -0500 |
commit | d743136c6b98fa91f501cc15dc47530f8f93b8fe (patch) | |
tree | aedb9af30e41f056aea148cc9e3b37d17327ca54 /nailgun_launcher | |
parent | e2cb8726735ba306ade3befefae3b87549b52c9f (diff) | |
parent | f5e653e033e468c58e8a832e0dce3a7020a66ce4 (diff) | |
download | cbt-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
Diffstat (limited to 'nailgun_launcher')
-rw-r--r-- | nailgun_launcher/BuildStage1Result.java | 8 | ||||
-rw-r--r-- | nailgun_launcher/CbtURLClassLoader.java | 33 | ||||
-rw-r--r-- | nailgun_launcher/ClassLoaderCache.java | 66 | ||||
-rw-r--r-- | nailgun_launcher/ClassLoaderCache2.java | 37 | ||||
-rw-r--r-- | nailgun_launcher/EarlyDependencies.java | 210 | ||||
-rw-r--r-- | nailgun_launcher/NailgunLauncher.java | 128 | ||||
-rw-r--r-- | nailgun_launcher/Stage0Lib.java | 67 | ||||
-rw-r--r-- | nailgun_launcher/TrapSecurityManager.java | 2 |
8 files changed, 327 insertions, 224 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 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? |