package cbt; import java.io.*; import java.lang.reflect.*; import java.net.*; import java.nio.*; import java.nio.file.*; import java.security.*; import java.util.*; import javax.xml.bind.annotation.adapters.HexBinaryAdapter; 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; import cbt.reflect.TrapSystemExit; public class Stage0Lib{ 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{ return TrapSystemExit.run( new TrapSystemExit(){ @Override public Integer run() throws Throwable{ cl.loadClass(cls) .getMethod("main", String[].class) .invoke( null, (Object) args); return 0; } @Override public Integer wrap(int exitCode){ return exitCode; } } ); } public static Object get(Object object, String method) throws Throwable{ return object.getClass().getMethod( method ).invoke(object); } public static String classpath( String... files ){ Arrays.sort(files); return mkString( pathSeparator, files ); } public static long lastModified( String... files ){ List lastModified = new ArrayList(); 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 long compile( long lastModified, String classpath, String target, EarlyDependencies earlyDeps, List sourceFiles ) throws Throwable{ File statusFile = new File( new File(target) + ".last-success" ); long lastCompiled = statusFile.lastModified(); long maxLastModified = lastModified; final long start = System.currentTimeMillis(); // <- before recursing, so we catch them all for( File file: sourceFiles ){ 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( maxLastModified > lastCompiled ){ List zincArgs = new ArrayList( Arrays.asList( new String[]{ "-scala-compiler", earlyDeps.scalaCompiler_File, "-scala-library", earlyDeps.scalaLibrary_File, "-scala-extra", earlyDeps.scalaReflect_File, "-sbt-interface", earlyDeps.sbtInterface_File, "-compiler-interface", earlyDeps.compilerInterface_File, "-cp", classpath, "-d", target, "-S-deprecation", "-S-feature", "-S-unchecked", "-S-language:existentials" } ) ); for( File f: sourceFiles ){ zincArgs.add(f.toString()); } PrintStream oldOut = System.out; try{ System.setOut(System.err); int exitCode = runMain( "com.typesafe.zinc.Main", zincArgs.toArray(new String[zincArgs.size()]), earlyDeps.zinc ); if( exitCode == 0 ){ write( statusFile, "" ); Files.setLastModifiedTime( statusFile.toPath(), FileTime.fromMillis(start) ); } else { System.exit( exitCode ); } } 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; } } public static ClassLoader classLoader( String file ) throws Throwable{ return new CbtURLClassLoader( new URL[]{ new URL("file:"+file) } ); } public static ClassLoader classLoader( String file, ClassLoader parent ) throws Throwable{ return new CbtURLClassLoader( new URL[]{ new URL("file:"+file) }, parent ); } private static String getVarFromEnv(String envKey) { String value = System.getenv(envKey); if(value == null || value.isEmpty()) { value = System.getenv(envKey.toUpperCase()); } return value; } 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())) { String envVar = getVarFromEnv(envKey); if(envVar != null && !envVar.isEmpty()) { String[] proxy = envVar.replaceFirst("^https?://", "").split(":", 2); System.setProperty(propKeyH, proxy[0]); System.setProperty(propKeyP, proxy[1]); } } } public static void installProxySettings() throws URISyntaxException { 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()) { String envVar = getVarFromEnv("no_proxy"); if(envVar != null && !envVar.isEmpty()) { System.setProperty("http.nonProxyHosts", envVar.replaceAll(",","|")); } } } private static final ProxySelector ps = ProxySelector.getDefault(); public static HttpURLConnection openConnectionConsideringProxy(URL urlString) throws IOException, URISyntaxException { java.net.Proxy proxy = ps.select(urlString.toURI()).get(0); return (HttpURLConnection) urlString.openConnection(proxy); } public static void download(URL urlString, Path target, String sha1) throws Throwable { 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 = openConnectionConsideringProxy(urlString).getInputStream(); Files.copy(stream, unverified, StandardCopyOption.REPLACE_EXISTING); stream.close(); final String checksum = sha1(Files.readAllBytes(unverified)); if(sha1 == null || sha1.toLowerCase().equals(checksum)) { Files.move(unverified, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); } else { System.err.println(unverified + " checksum does not match.\nExpected: |" + sha1 + "|\nFound: |" + checksum + "|"); System.exit(1); } } } public static String sha1(byte[] bytes) throws Throwable { final MessageDigest sha1 = MessageDigest.getInstance("SHA1"); sha1.update(bytes, 0, bytes.length); return (new HexBinaryAdapter()).marshal(sha1.digest()).toLowerCase(); } public static String mkString(String separator, Object[] parts){ String result = parts[0].toString(); for(int i = 1; i < parts.length; i++){ result += separator + parts[i].toString(); } return result; } public static String[] append( String[] array, String item ){ String[] copy = Arrays.copyOf(array, array.length + 1); copy[array.length] = item; return copy; } public static String[] concat( String[] left, String[] right ){ String[] result = Arrays.copyOf(left, left.length + right.length); System.arraycopy(right, 0, result, left.length, right.length); return result; } }