From 2c20a0dddc70a5eee207fb1c588bfd53eaaa7841 Mon Sep 17 00:00:00 2001 From: Christopher Vogt Date: Wed, 16 Mar 2016 02:22:01 -0400 Subject: merged most bootstrapping logic into launcher --- bootstrap_scala/BootstrapScala.java | 85 ----------------- bootstrap_scala/Dependency.java | 29 ------ bootstrap_scala/bootstrap_scala | 27 ------ cbt | 42 +-------- nailgun_launcher/CBTUrlClassLoader.java | 31 ++++++ nailgun_launcher/Dependency.java | 29 ++++++ nailgun_launcher/NailgunLauncher.java | 162 ++++++++++++++++++++++++-------- stage1/constants.scala | 2 +- stage1/paths.scala | 2 +- 9 files changed, 190 insertions(+), 219 deletions(-) delete mode 100644 bootstrap_scala/BootstrapScala.java delete mode 100644 bootstrap_scala/Dependency.java delete mode 100755 bootstrap_scala/bootstrap_scala create mode 100644 nailgun_launcher/CBTUrlClassLoader.java create mode 100644 nailgun_launcher/Dependency.java diff --git a/bootstrap_scala/BootstrapScala.java b/bootstrap_scala/BootstrapScala.java deleted file mode 100644 index e2d7a5a..0000000 --- a/bootstrap_scala/BootstrapScala.java +++ /dev/null @@ -1,85 +0,0 @@ -import java.io.File; -import java.io.InputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Iterator; -import javax.xml.bind.annotation.adapters.HexBinaryAdapter; - -/** - * This file class allows bootstrapping out of Java into Scala. It downloads the Scala jars for the - * version number given as the first argument into the directory given as the second argument and - * returns a classpath String. - */ -public class BootstrapScala { - - public final static Dependency[] dependencies(String target, String scalaVersion) throws MalformedURLException { - return new Dependency[] { - Dependency.scala(target, scalaVersion, "library", "DDD5A8BCED249BEDD86FB4578A39B9FB71480573"), - Dependency.scala(target, scalaVersion, "compiler","FE1285C9F7B58954C5EF6D80B59063569C065E9A"), - Dependency.scala(target, scalaVersion, "reflect", "B74530DEEBA742AB4F3134DE0C2DA0EDC49CA361"), - new Dependency(target, "modules/scala-xml_2.11/1.0.5", "scala-xml_2.11-1.0.5", "77ac9be4033768cf03cc04fbd1fc5e5711de2459") - }; - } - - public static void main(String args[]) throws IOException, NoSuchAlgorithmException { - - if(args.length < 2){ - System.err.println("Usage: bootstrap_scala "); - System.exit(1); - } - - Dependency[] ds = dependencies( args[1], args[0] ); - new File(args[1]).mkdirs(); - for (Dependency d: ds) { - download( d.url, d.path, d.hash ); - } - - // Join dep. paths as a classpath - String classpath = ""; - Iterator depsIter = Arrays.asList(ds).iterator(); - while (depsIter.hasNext()) { - Dependency dep = depsIter.next(); - classpath += dep.path.toString(); - if (depsIter.hasNext()) { - classpath += File.pathSeparator; - } - } - - System.out.println(classpath); - - } - - 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 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()); - } - -} diff --git a/bootstrap_scala/Dependency.java b/bootstrap_scala/Dependency.java deleted file mode 100644 index 571047b..0000000 --- a/bootstrap_scala/Dependency.java +++ /dev/null @@ -1,29 +0,0 @@ -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; - -class Dependency { - - final URL url; - final Path path; - final String hash; - - public Dependency(String target, String folder, String file, String hash) throws MalformedURLException { - this.path = Paths.get(target + file + ".jar"); - this.url = new URL("https://repo1.maven.org/maven2/org/scala-lang/" + folder + "/" + file + ".jar"); - this.hash = hash; - } - - // scala-lang dependency - public static Dependency scala(String target, String scalaVersion, String scalaModule, String hash) - throws MalformedURLException { - return new Dependency( - target, - "scala-" + scalaModule + "/" + scalaVersion, - "scala-" + scalaModule + "-" + scalaVersion, - hash - ); - } - -} diff --git a/bootstrap_scala/bootstrap_scala b/bootstrap_scala/bootstrap_scala deleted file mode 100755 index b004c8d..0000000 --- a/bootstrap_scala/bootstrap_scala +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -_DIR=$(dirname $(readlink "$0") 2>/dev/null || dirname "$0" 2>/dev/null ) -DIR=$(dirname $($_DIR/../realpath/realpath.sh $0)) -JAVAC="javac -Xlint:deprecation" -TARGET=$DIR/target -CLASSES=$TARGET/classes/ -VERSION=$1 -CACHE=$DIR/cache/$VERSION/ - -COMPILER_JAR=scala-compiler-$VERSION.jar -LIBRARY_JAR=scala-library-$VERSION.jar -REFLECT_JAR=scala-reflect-$VERSION.jar -XML_JAR=scala-xml_2.11-1.0.5.jar # this is a bit fishy, because it doesn't take version into account - -mkdir -p $CLASSES - -if [ ! -f $CACHE$COMPILER_JAR ] || [ ! -f $CACHE$LIBRARY_JAR ] || [ ! -f $CACHE$REFLECT_JAR ]\ - || [ ! -f $CACHE$XML_JAR ] || [ $DIR/BootstrapScala.java -nt $CLASSES/BootstrapScala.class ] || [ $DIR/Dependency.java -nt $CLASSES/Dependency.class ] -then - echo "Compiling cbt/bootstrap_scala" 1>&2 - $JAVAC -d $CLASSES $DIR/BootstrapScala.java $DIR/Dependency.java - java -cp $CLASSES BootstrapScala $1 $CACHE -else - # for speedup - echo `for f in $CACHE*; do printf "$f "; done`|tr " " ":" -fi diff --git a/cbt b/cbt index a5feb54..372c145 100755 --- a/cbt +++ b/cbt @@ -78,12 +78,9 @@ log "Find out real path. Build realpath if needed." $* export CBT_HOME=$(dirname $($_DIR/realpath/realpath.sh $0)) -export SCALA_VERSION="2.11.8" export NAILGUN=$CBT_HOME/nailgun_launcher/ -export STAGE1=$CBT_HOME/stage1/ export TARGET=target/scala-2.11/classes/ mkdir -p $NAILGUN$TARGET -mkdir -p $STAGE1$TARGET nailgun_out=$NAILGUN/target/nailgun.stdout.log nailgun_err=$NAILGUN/target/nailgun.strerr.log @@ -135,18 +132,6 @@ if [ $use_nailgun -eq 0 ] && [ ! $server_up -eq 0 ]; then ng-server 127.0.0.1:$NAILGUN_PORT >> $nailgun_out 2>> $nailgun_err & fi -log "Downloading Scala jars if necessary..." $* -export SCALA_CLASSPATH=`$CBT_HOME/bootstrap_scala/bootstrap_scala $SCALA_VERSION` -if [ ! $? -eq 0 ]; then echo "Problem with bootstrap_scala" 1>&2; exit 1; fi - -SCALAC="java -Xmx256M -Xms32M\ - -Xbootclasspath/a:$SCALA_CLASSPATH\ - -Dscala.usejavacp=true\ - -Denv.emacs=\ - scala.tools.nsc.Main\ - -deprecation\ - -feature" - stage1 () { log "Checking for changes in cbt/nailgun_launcher" $* NAILGUN_INDICATOR=$NAILGUN$TARGET/cbt/NailgunLauncher.class @@ -172,29 +157,8 @@ stage1 () { fi fi - log "Checking for changes in cbt/stage1" $* - STAGE1_INDICATOR=$STAGE1$TARGET/cbt/Stage1.class - changed2=0 - for file in `ls $STAGE1*.scala`; do - if [ $file -nt $STAGE1_INDICATOR ]; then changed2=1; fi - done - compiles2=0 - - if [ $changed2 -eq 1 ]; then - rm $STAGE1$TARGET/cbt/*.class 2>/dev/null # defensive delete of potentially broken class files - echo "Compiling cbt/stage1" 1>&2 - $SCALAC -cp $NAILGUN$TARGET -d $STAGE1$TARGET `ls $STAGE1/*.scala` - compiles2=$? - if [ $compiles2 -ne 0 ]; then - rm $STAGE1$TARGET/cbt/*.class 2>/dev/null # triggers recompilation next time. - break - fi - fi - log "run CBT and loop if desired. This allows recompiling CBT itself as part of compile looping." $* - mainClass=cbt.Stage1 - CP=$STAGE1$TARGET:$SCALA_CLASSPATH if [ $use_nailgun -eq 1 ] then log "Running JVM directly" $* @@ -221,11 +185,11 @@ stage1 () { fi sleep 0.3 done - log "Running $mainClass via Nailgun." $* + log "Running CBT via Nailgun." $* $NG cbt.NailgunLauncher "$CWD" $* fi exitCode=$? - log "Done running $mainClass." $* + log "Done running CBT." $* } while true; do @@ -236,7 +200,7 @@ while true; do echo "======= Restarting CBT =======" 1>&2 done -if [ $compiles -ne 0 ] || [ $compiles2 -ne 0 ]; then +if [ $compiles -ne 0 ]; then exitCode=1 fi 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 scalaClassPath = new ArrayList(); + + 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 scalacArgsList = new ArrayList( + 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 urls = new ArrayList(); + 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()); } } + + diff --git a/stage1/constants.scala b/stage1/constants.scala index a14754e..147e10c 100644 --- a/stage1/constants.scala +++ b/stage1/constants.scala @@ -1,5 +1,5 @@ package cbt object constants{ - val scalaVersion = Option(System.getenv("SCALA_VERSION")).get + val scalaVersion = NailgunLauncher.SCALA_VERSION val scalaMajorVersion = scalaVersion.split("\\.").take(2).mkString(".") } diff --git a/stage1/paths.scala b/stage1/paths.scala index 2006ae8..e8e3cc5 100644 --- a/stage1/paths.scala +++ b/stage1/paths.scala @@ -6,7 +6,7 @@ object paths{ val userHome: File = new File(Option(System.getProperty("user.home")).get) val bootstrapScala: File = cbtHome ++ "/bootstrap_scala" val nailgun: File = new File(Option(System.getenv("NAILGUN")).get) - val stage1: File = new File(Option(System.getenv("STAGE1")).get) + val stage1: File = new File(NailgunLauncher.STAGE1) val stage2: File = cbtHome ++ "/stage2" private val target = Option(System.getenv("TARGET")).get.stripSuffix("/") val stage1Target: File = stage1 ++ ("/" ++ target) -- cgit v1.2.3