aboutsummaryrefslogtreecommitdiff
path: root/nailgun_launcher
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2016-03-16 02:22:01 -0400
committerChristopher Vogt <oss.nsp@cvogt.org>2016-03-20 22:12:06 -0400
commit2c20a0dddc70a5eee207fb1c588bfd53eaaa7841 (patch)
tree8ae901ccafdc509ebb9717b116fdfc4f0244773f /nailgun_launcher
parent450fc5d3defcdc279cfeef0ae622ebe4f90988e2 (diff)
downloadcbt-2c20a0dddc70a5eee207fb1c588bfd53eaaa7841.tar.gz
cbt-2c20a0dddc70a5eee207fb1c588bfd53eaaa7841.tar.bz2
cbt-2c20a0dddc70a5eee207fb1c588bfd53eaaa7841.zip
merged most bootstrapping logic into launcher
Diffstat (limited to 'nailgun_launcher')
-rw-r--r--nailgun_launcher/CBTUrlClassLoader.java31
-rw-r--r--nailgun_launcher/Dependency.java29
-rw-r--r--nailgun_launcher/NailgunLauncher.java162
3 files changed, 185 insertions, 37 deletions
diff --git a/nailgun_launcher/CBTUrlClassLoader.java b/nailgun_launcher/CBTUrlClassLoader.java
new file mode 100644
index 0000000..72423a0
--- /dev/null
+++ b/nailgun_launcher/CBTUrlClassLoader.java
@@ -0,0 +1,31 @@
+package cbt;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+class CbtURLClassLoader extends URLClassLoader{
+ public String toString(){
+ return (
+ super.toString()
+ + "(\n "
+ + Arrays.toString(getURLs())
+ + ",\n "
+ + String.join("\n ",getParent().toString().split("\n"))
+ + "\n)"
+ );
+ }
+ void assertExist(URL[] urls){
+ for(URL url: urls){
+ if(!new File(url.getPath()).exists()){
+ throw new AssertionError("File does not exist when trying to create CbtURLClassLoader: "+url);
+ }
+ }
+ }
+ public CbtURLClassLoader(URL[] urls, ClassLoader parent){
+ super(urls, parent);
+ assertExist(urls);
+ }
+ public CbtURLClassLoader(URL[] urls){
+ super(urls);
+ assertExist(urls);
+ }
+} \ No newline at end of file
diff --git a/nailgun_launcher/Dependency.java b/nailgun_launcher/Dependency.java
new file mode 100644
index 0000000..93f4785
--- /dev/null
+++ b/nailgun_launcher/Dependency.java
@@ -0,0 +1,29 @@
+package cbt;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+class EarlyDependency {
+
+ final URL url;
+ final Path path;
+ final String hash;
+
+ public EarlyDependency(String folder, String file, String hash) throws MalformedURLException {
+ this.path = Paths.get(NailgunLauncher.CBT_HOME + "/cache/maven/" + folder + "/" + file + ".jar");
+ this.url = new URL("https://repo1.maven.org/maven2/" + folder + "/" + file + ".jar");
+ this.hash = hash;
+ }
+
+ // scala-lang dependency
+ public static EarlyDependency scala(String scalaModule, String hash)
+ throws MalformedURLException {
+ return new EarlyDependency(
+ "org/scala-lang/scala-" + scalaModule + "/" + NailgunLauncher.SCALA_VERSION,
+ "scala-" + scalaModule + "-" + NailgunLauncher.SCALA_VERSION,
+ hash
+ );
+ }
+
+}
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java
index cd9e499..9121797 100644
--- a/nailgun_launcher/NailgunLauncher.java
+++ b/nailgun_launcher/NailgunLauncher.java
@@ -4,16 +4,15 @@ import java.lang.reflect.*;
import java.net.*;
import java.nio.*;
import java.nio.file.*;
+import java.security.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
/**
* This launcher allows to start the JVM without loading anything else permanently into its
* classpath except for the launcher itself. That's why it is written in Java without
* dependencies outside the JDK.
- *
- * The main method loads the given class from the given class path, calls it's main
- * methods passing in the additional arguments.
*/
public class NailgunLauncher{
@@ -26,30 +25,100 @@ public class NailgunLauncher{
public static SecurityManager defaultSecurityManager = System.getSecurityManager();
+ public static String CBT_HOME = System.getenv("CBT_HOME");
+ public static String NAILGUN = System.getenv("NAILGUN");
+ public static String STAGE1 = CBT_HOME + "/stage1/";
+ public static String TARGET = System.getenv("TARGET");
+
+ public static String SCALA_VERSION = "2.11.8";
+
+ public static void _assert(Boolean condition, Object msg){
+ if(!condition){
+ throw new AssertionError("Assertion failed: "+msg);
+ }
+ }
+
public static void main(String[] args) throws ClassNotFoundException,
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
- MalformedURLException {
- String CBT_HOME = System.getenv("CBT_HOME");
- String SCALA_VERSION = System.getenv("SCALA_VERSION");
- String NAILGUN = System.getenv("NAILGUN");
- String STAGE1 = System.getenv("STAGE1");
- String TARGET = System.getenv("TARGET");
- assert(CBT_HOME != null);
- assert(SCALA_VERSION != null);
- assert(NAILGUN != null);
- assert(STAGE1 != null);
- assert(TARGET != null);
-
- String library = CBT_HOME+"/bootstrap_scala/cache/"+SCALA_VERSION+"/scala-library-"+SCALA_VERSION+".jar";
+ MalformedURLException,
+ IOException,
+ NoSuchAlgorithmException {
+ _assert(CBT_HOME != null, CBT_HOME);
+ _assert(NAILGUN != null, NAILGUN);
+ _assert(TARGET != null, TARGET);
+ _assert(STAGE1 != null, STAGE1);
+
+ File f2 = new File(STAGE1);
+ _assert(f2.listFiles() != null, f2);
+ long lastCompiled = new File(STAGE1 + TARGET + "/cbt/Stage1.class").lastModified();
+ for( File file: f2.listFiles() ){
+ if( file.isFile() && file.toString().endsWith(".scala")
+ && file.lastModified() > lastCompiled ){
+
+ EarlyDependency[] dependencies = new EarlyDependency[]{
+ EarlyDependency.scala("library", "DDD5A8BCED249BEDD86FB4578A39B9FB71480573"),
+ EarlyDependency.scala("compiler","FE1285C9F7B58954C5EF6D80B59063569C065E9A"),
+ EarlyDependency.scala("reflect", "B74530DEEBA742AB4F3134DE0C2DA0EDC49CA361"),
+ new EarlyDependency("org/scala-lang/modules/scala-xml_2.11/1.0.5", "scala-xml_2.11-1.0.5", "77ac9be4033768cf03cc04fbd1fc5e5711de2459")
+ };
+
+ ArrayList<String> scalaClassPath = new ArrayList<String>();
+
+ for (EarlyDependency d: dependencies) {
+ download( d.url, d.path, d.hash );
+ scalaClassPath.add( d.path.toString() );
+ }
+
+ File stage1ClassFiles = new File(STAGE1 + TARGET + "/cbt/");
+ if( stage1ClassFiles.exists() ){
+ for( File f: stage1ClassFiles.listFiles() ){
+ if( f.toString().endsWith(".class") ){
+ f.delete();
+ }
+ }
+ }
+
+ new File(STAGE1 + TARGET).mkdirs();
+
+ String s = File.pathSeparator;
+ ArrayList<String> scalacArgsList = new ArrayList<String>(
+ Arrays.asList(
+ new String[]{
+ "-deprecation", "-feature",
+ "-cp", String.join( s, scalaClassPath.toArray(new String[scalaClassPath.size()])) + s + NAILGUN+TARGET,
+ "-d", STAGE1+TARGET
+ }
+ )
+ );
+
+ for( File f: new File(STAGE1).listFiles() ){
+ if( f.isFile() && f.toString().endsWith(".scala") ){
+ scalacArgsList.add( f.toString() );
+ }
+ }
+
+ ArrayList<URL> urls = new ArrayList<URL>();
+ for( String c: scalaClassPath ){
+ urls.add(new URL("file:"+c));
+ }
+ ClassLoader cl = new CbtURLClassLoader( (URL[]) urls.toArray(new URL[urls.size()]) );
+ cl.loadClass("scala.tools.nsc.Main")
+ .getMethod("main", String[].class)
+ .invoke( null/* _cls.newInstance()*/, (Object) scalacArgsList.toArray(new String[scalacArgsList.size()]));
+ break;
+ }
+ }
+
+ String library = CBT_HOME+"/cache/maven/org/scala-lang/scala-library/"+SCALA_VERSION+"/scala-library-"+SCALA_VERSION+".jar";
if(!classLoaderCacheKeys.containsKey(library)){
Object libraryKey = new Object();
classLoaderCacheKeys.put(library,libraryKey);
ClassLoader libraryClassLoader = new CbtURLClassLoader( new URL[]{ new URL("file:"+library) } );
classLoaderCacheValues.put(libraryKey, libraryClassLoader);
- String xml = CBT_HOME+"/bootstrap_scala/cache/"+SCALA_VERSION+"/scala-xml_2.11-1.0.5.jar";
+ String xml = CBT_HOME+"/cache/maven/org/scala-lang/modules/scala-xml_2.11/1.0.5/scala-xml_2.11-1.0.5.jar";
Object xmlKey = new Object();
classLoaderCacheKeys.put(xml,xmlKey);
ClassLoader xmlClassLoader = new CbtURLClassLoader(
@@ -72,34 +141,53 @@ public class NailgunLauncher{
return;
}
- ClassLoader cl = new URLClassLoader(
+ ClassLoader cl = new CbtURLClassLoader(
new URL[]{ new URL("file:"+STAGE1+TARGET) },
classLoaderCacheValues.get(
classLoaderCacheKeys.get( NAILGUN+TARGET )
)
);
- cl.loadClass("cbt.Stage1")
- .getMethod("main", String[].class, ClassLoader.class)
- .invoke( null/* _cls.newInstance()*/, (Object) args, cl);
+ try{
+ cl.loadClass("cbt.Stage1")
+ .getMethod("main", String[].class, ClassLoader.class)
+ .invoke( null/* _cls.newInstance()*/, (Object) args, cl);
+ }catch(ClassNotFoundException e){
+ System.err.println(cl);
+ throw e;
+ }catch(NoClassDefFoundError e){
+ System.err.println(cl);
+ throw e;
+ }catch(InvocationTargetException e){
+ System.err.println(cl);
+ throw e;
+ }
}
-}
-class CbtURLClassLoader extends URLClassLoader{
- public String toString(){
- return (
- super.toString()
- + "(\n "
- + Arrays.toString(getURLs())
- + ",\n "
- + String.join("\n ",getParent().toString().split("\n"))
- + "\n)"
- );
- }
- public CbtURLClassLoader(URL[] urls, ClassLoader parent){
- super(urls, parent);
+ public static void download(URL urlString, Path target, String sha1) throws IOException, NoSuchAlgorithmException {
+ final Path unverified = Paths.get(target+".unverified");
+ if(!Files.exists(target)) {
+ new File(target.toString()).getParentFile().mkdirs();
+ System.err.println("downloading " + urlString);
+ System.err.println("to " + target);
+ final InputStream stream = urlString.openStream();
+ Files.copy(stream, unverified, StandardCopyOption.REPLACE_EXISTING);
+ stream.close();
+ final String checksum = sha1(Files.readAllBytes(unverified));
+ if(sha1 == null || sha1.toUpperCase().equals(checksum)) {
+ Files.move(unverified, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ } else {
+ System.err.println(target + " checksum does not match.\nExpected: |" + sha1 + "|\nFound: |" + checksum + "|");
+ System.exit(1);
+ }
+ }
}
- public CbtURLClassLoader(URL[] urls){
- super(urls);
+
+ public static String sha1(byte[] bytes) throws NoSuchAlgorithmException {
+ final MessageDigest sha1 = MessageDigest.getInstance("SHA1");
+ sha1.update(bytes, 0, bytes.length);
+ return (new HexBinaryAdapter()).marshal(sha1.digest());
}
}
+
+