diff --git a/scala/support/BUILD b/scala/support/BUILD index 3f29921ee..72c1a5939 100644 --- a/scala/support/BUILD +++ b/scala/support/BUILD @@ -1,7 +1,13 @@ load("//scala:scala.bzl", "scala_library") + +java_import( + name = "scala_xml", + jars = ["@scala//:lib/scala-xml_2.11-1.0.4.jar"] +) + scala_library(name = "test_reporter", srcs = ["JUnitXmlReporter.scala"], - deps = ["@scalatest//file"], + deps = ["@scalatest//file", ":scala_xml"], visibility = ["//visibility:public"], ) diff --git a/src/java/io/bazel/rulesscala/scalac/BUILD b/src/java/io/bazel/rulesscala/scalac/BUILD index 1ebf9ea5d..04b15ffbc 100644 --- a/src/java/io/bazel/rulesscala/scalac/BUILD +++ b/src/java/io/bazel/rulesscala/scalac/BUILD @@ -1,6 +1,6 @@ java_binary(name = "scalac", main_class = "io.bazel.rulesscala.scalac.ScalaCInvoker", - srcs = ["ScalaCInvoker.java"], + srcs = ["ScalaCInvoker.java", "CompileOptions.java"], deps = [ "@scala//:lib/scala-library.jar", diff --git a/src/java/io/bazel/rulesscala/scalac/CompileOptions.java b/src/java/io/bazel/rulesscala/scalac/CompileOptions.java new file mode 100644 index 000000000..350f8441f --- /dev/null +++ b/src/java/io/bazel/rulesscala/scalac/CompileOptions.java @@ -0,0 +1,100 @@ +package io.bazel.rulesscala.scalac; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CompileOptions { + final public String outputName; + final public String manifestPath; + final public String[] scalaOpts; + final public String[] pluginArgs; + final public String classpath; + final public String[] files; + final public String[] sourceJars; + final public boolean iJarEnabled; + final public String ijarOutput; + final public String ijarCmdPath; + + public CompileOptions(List args) { + Map argMap = buildArgMap(args); + + outputName = getOrError(argMap, "JarOutput", "Missing required arg JarOutput"); + manifestPath = getOrError(argMap, "Manifest", "Missing required arg Manifest"); + + scalaOpts = getOrEmpty(argMap, "ScalacOpts").split(","); + pluginArgs = buildPluginArgs(getOrEmpty(argMap, "Plugins")); + classpath = getOrError(argMap, "Classpath", "Must supply the classpath arg"); + files = getOrEmpty(argMap, "Files").split(","); + + sourceJars = getOrEmpty(argMap, "SourceJars").split(","); + iJarEnabled = booleanGetOrFalse(argMap, "EnableIjar"); + if(iJarEnabled) { + ijarOutput = getOrError(argMap, "ijarOutput", "Missing required arg ijarOutput when ijar enabled"); + ijarCmdPath = getOrError(argMap, "ijarCmdPath", "Missing required arg ijarCmdPath when ijar enabled"); + } + else { + ijarOutput = null; + ijarCmdPath = null; + } + } + + private static HashMap buildArgMap(List lines) { + HashMap hm = new HashMap(); + for(String line: lines) { + String[] lSplit = line.split(": "); + if(lSplit.length > 2) { + throw new RuntimeException("Bad arg, should have at most 1 space/2 spans. arg: " + line); + } + if(lSplit.length > 1) { + hm.put(lSplit[0], lSplit[1]); + } + } + return hm; + } + + private static String getOrEmpty(Map m, String k) { + if(m.containsKey(k)) { + return m.get(k); + } else { + return ""; + } + } + private static String getOrError(Map m, String k, String errorMessage) { + if(m.containsKey(k)) { + return m.get(k); + } else { + throw new RuntimeException(errorMessage); + } + } + + private static boolean booleanGetOrFalse(Map m, String k) { + if(m.containsKey(k)) { + String v = m.get(k); + if(v.trim().equals("True") || v.trim().equals("true")) { + return true; + } + } + return false; + } + public static String[] buildPluginArgs(String packedPlugins) { + String[] pluginElements = packedPlugins.split(","); + int numPlugins = 0; + for(int i =0; i< pluginElements.length; i++){ + if(pluginElements[i].length() > 0) { + numPlugins += 1; + } + } + + String[] result = new String[numPlugins]; + int idx = 0; + for(int i =0; i< pluginElements.length; i++){ + if(pluginElements[i].length() > 0) { + result[idx] = "-Xplugin:" + pluginElements[i]; + idx += 1; + } + } + return result; + } +} diff --git a/src/java/io/bazel/rulesscala/scalac/ScalaCInvoker.java b/src/java/io/bazel/rulesscala/scalac/ScalaCInvoker.java index ca03f7459..e56406db4 100644 --- a/src/java/io/bazel/rulesscala/scalac/ScalaCInvoker.java +++ b/src/java/io/bazel/rulesscala/scalac/ScalaCInvoker.java @@ -14,30 +14,6 @@ package io.bazel.rulesscala.scalac; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Collection; -import java.util.Map; -import java.util.HashMap; -import java.util.TreeMap; -import java.util.jar.Attributes; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; -import scala.tools.nsc.*; -import java.io.*; -import java.lang.reflect.Field; -import scala.tools.nsc.reporters.ConsoleReporter; -import java.util.Arrays; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.FileSystems; -import io.bazel.rulesscala.jar.JarCreator; -import static java.nio.charset.StandardCharsets.UTF_8; - import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -45,53 +21,44 @@ import com.google.devtools.build.lib.worker.WorkerProtocol.Input; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; - +import io.bazel.rulesscala.jar.JarCreator; +import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Field; +import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; +import java.util.Map; +import java.util.TreeMap; import java.util.UUID; +import java.util.jar.Attributes; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; import scala.Console$; +import scala.tools.nsc.*; +import scala.tools.nsc.reporters.ConsoleReporter; +import static java.nio.charset.StandardCharsets.UTF_8; + /** * A class for creating Jar files. Allows normalization of Jar entries by setting their timestamp to * the DOS epoch. All Jar entries are sorted alphabetically. */ public class ScalaCInvoker { - private static List extractJar(String jarPath, String outputFolder) throws IOException, FileNotFoundException{ - List outputPaths = new ArrayList(); - java.util.jar.JarFile jar = new java.util.jar.JarFile(jarPath); -java.util.Enumeration e = jar.entries(); -while (e.hasMoreElements()) { - java.util.jar.JarEntry file = (java.util.jar.JarEntry) e.nextElement(); - java.io.File f = new java.io.File(outputFolder + java.io.File.separator + file.getName()); - - if (file.isDirectory()) { // if its a directory, create it - f.mkdirs(); - continue; - } - - File parent = f.getParentFile(); - parent.mkdirs(); - outputPaths.add(f); - - java.io.InputStream is = jar.getInputStream(file); // get the input stream - java.io.FileOutputStream fos = new java.io.FileOutputStream(f); - while (is.available() > 0) { // write contents of 'is' to 'fos' - fos.write(is.read()); - } - fos.close(); - is.close(); -} -return outputPaths; -} - - - // A UUID that uniquely identifies this running worker process. + // A UUID that uniquely identifies this running worker process. static final UUID workerUuid = UUID.randomUUID(); // A counter that increases with each work unit processed. @@ -103,7 +70,6 @@ private static List extractJar(String jarPath, String outputFolder) throws // Keep state across multiple builds. static final LinkedHashMap inputs = new LinkedHashMap<>(); - static class WorkerOptions { public int exitAfter = 30; public int poisonAfter = 30; @@ -170,28 +136,62 @@ private static void runPersistentWorker(WorkerOptions workerOptions) throws IOEx } } + static private String[] extractSourceJars(CompileOptions opts, Path tmpParent) throws IOException { + List sourceFiles = new ArrayList(); - - public static String[] buildPluginArgs(String packedPlugins) { - String[] pluginElements = packedPlugins.split(","); - int numPlugins = 0; - for(int i =0; i< pluginElements.length; i++){ - if(pluginElements[i].length() > 0) { - numPlugins += 1; + for(String jarPath : opts.sourceJars) { + if (jarPath.length() > 0){ + Path tmpPath = Files.createTempDirectory(tmpParent, "tmp"); + sourceFiles.addAll(extractJar(jarPath, tmpPath.toString())); } } + String[] files = appendToString(opts.files, sourceFiles); + if(files.length == 0) { + throw new RuntimeException("Must have input files from either source jars or local files."); + } + return files; + } + + private static List extractJar(String jarPath, + String outputFolder) throws IOException, FileNotFoundException { - String[] result = new String[numPlugins]; - int idx = 0; - for(int i =0; i< pluginElements.length; i++){ - if(pluginElements[i].length() > 0) { - result[idx] = "-Xplugin:" + pluginElements[i]; - idx += 1; + List outputPaths = new ArrayList(); + java.util.jar.JarFile jar = new java.util.jar.JarFile(jarPath); + java.util.Enumeration e = jar.entries(); + while (e.hasMoreElements()) { + java.util.jar.JarEntry file = (java.util.jar.JarEntry) e.nextElement(); + File f = new File(outputFolder + java.io.File.separator + file.getName()); + + if (file.isDirectory()) { // if its a directory, create it + f.mkdirs(); + continue; } + + File parent = f.getParentFile(); + parent.mkdirs(); + outputPaths.add(f); + + java.io.InputStream is = jar.getInputStream(file); // get the input stream + java.io.FileOutputStream fos = new java.io.FileOutputStream(f); + while (is.available() > 0) { // write contents of 'is' to 'fos' + fos.write(is.read()); + } + fos.close(); + is.close(); } - return result; + return outputPaths; } + static String[] appendToString(String[] init, List rest) { + String[] tmp = new String[init.length + rest.size()]; + System.arraycopy(init, 0, tmp, 0, init.length); + int baseIdx = init.length; + for(T t : rest) { + tmp[baseIdx] = t.toString(); + baseIdx += 1; + } + return tmp; + } public static String[] merge(String[]... arrays) { int totalLength = 0; for(String[] arr:arrays){ @@ -207,72 +207,17 @@ public static String[] merge(String[]... arrays) { return result; } - - - // public static void dispatch(String[] args) throws Exception { - // if (ImmutableSet.copyOf(args).contains("--persistent_worker")) { - // OptionsParser parser = OptionsParser.newOptionsParser(ExampleWorkerOptions.class); - // parser.setAllowResidue(false); - // parser.parse(args); - // ExampleWorkerOptions workerOptions = parser.getOptions(ExampleWorkerOptions.class); - // Preconditions.checkState(workerOptions.persistentWorker); - - // runPersistentWorker(workerOptions); - // } else { - // // This is a single invocation of the example that exits after it processed the request. - // processRequest(ImmutableList.copyOf(args)); - // } - // } - private static HashMap buildArgMap(List lines) { - HashMap hm = new HashMap(); - for(String line: lines) { - String[] lSplit = line.split(" "); - if(lSplit.length > 2) { - throw new RuntimeException("Bad arg, should have at most 1 space/2 spans. arg: " + line); - } - if(lSplit.length > 1) { - String k = lSplit[0].substring(0, lSplit[0].length() - 1); - hm.put(k, lSplit[1]); - } - } - return hm; - } - - private static String getOrError(Map m, String k, String errorMessage) { - if(m.containsKey(k)) { - return m.get(k); - } else { - throw new RuntimeException(errorMessage); - } - } - - private static String getOrEmpty(Map m, String k) { - if(m.containsKey(k)) { - return m.get(k); - } else { - return ""; - } - } - - private static boolean booleanGetOrFalse(Map m, String k) { - if(m.containsKey(k)) { - String v = m.get(k); - if(v.trim().equals("True") || v.trim().equals("true")) { - return true; - } - } - return false; - } - -// ijarCmdPath: {ijar_cmd_path} + /** + * This is the reporter field for scalac, which we want to access + */ private static Field reporterField; static { try { - reporterField = Driver.class.getDeclaredField("reporter"); //NoSuchFieldException - reporterField.setAccessible(true); + reporterField = Driver.class.getDeclaredField("reporter"); //NoSuchFieldException + reporterField.setAccessible(true); } - catch (Exception ex){ - throw new RuntimeException("nope", ex); + catch (Exception ex) { + throw new RuntimeException("nope", ex); } } @@ -280,78 +225,25 @@ private static void processRequest(List args) throws Exception { if (args.size() == 1 && args.get(0).startsWith("@")) { args = Files.readAllLines(Paths.get(args.get(0).substring(1)), UTF_8); } + CompileOptions ops = new CompileOptions(args); - Map argMap = buildArgMap(args); - // for (Map.Entry entry : argMap.entrySet()) { - // System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); - // } - - String outputName = getOrError(argMap, "JarOutput", "Missing required arg JarOutput"); - String manifestPath = getOrError(argMap, "Manifest", "Missing required arg Manifest"); - - String[] scalaOpts = getOrEmpty(argMap, "ScalacOpts").split(","); - String[] pluginArgs = buildPluginArgs(getOrEmpty(argMap, "Plugins")); - String classpath = getOrError(argMap, "Classpath", "Must supply the classpath arg"); - String[] files = getOrEmpty(argMap, "Files").split(","); - Path outputPath = FileSystems.getDefault().getPath(outputName); - - String[] sourceJars = getOrEmpty(argMap, "SourceJars").split(","); - List sourceFiles = new ArrayList(); - - for(String jarPath : sourceJars) { - if(jarPath.length() > 0){ - Path tmpPath = Files.createTempDirectory(outputPath.getParent(), "tmp"); - sourceFiles.addAll(extractJar(jarPath, tmpPath.toString())); - } - } - - int sourceFilesSize = sourceFiles.size(); - String[] tmpFiles = new String[files.length + sourceFilesSize]; - System.arraycopy(files, 0, tmpFiles, 0, files.length); - int baseIdx = files.length; - for(File p: sourceFiles) { - tmpFiles[baseIdx] = p.toString(); - baseIdx += 1; - } - files = tmpFiles; - - if(files.length == 0) { - throw new Exception("Must have input files from either source jars or local files."); - } - - - boolean iJarEnabled = booleanGetOrFalse(argMap, "EnableIjar"); - String ijarOutput = null; - String ijarCmdPath = null; - if(iJarEnabled) { - ijarOutput = getOrError(argMap, "ijarOutput", "Missing required arg ijarOutput when ijar enabled"); - ijarCmdPath = getOrError(argMap, "ijarCmdPath", "Missing required arg ijarCmdPath when ijar enabled"); - } - - Path tmpPath = Files.createTempDirectory(outputPath.getParent(),"tmp"); - - + Path outputPath = FileSystems.getDefault().getPath(ops.outputName); + Path tmpPath = Files.createTempDirectory(outputPath.getParent(), "tmp"); String[] constParams = { "-classpath", - classpath, + ops.classpath, "-d", tmpPath.toString() }; String[] compilerArgs = merge( - scalaOpts, - pluginArgs, + ops.scalaOpts, + ops.pluginArgs, constParams, - files); + extractSourceJars(ops, outputPath.getParent())); MainClass comp = new MainClass(); - // System.out.println("\n\n\n\nCompiler args:::"); - // for(String arg: compilerArgs) { - // System.out.println(" " + arg); - // } - - long start = System.currentTimeMillis(); - + long start = System.currentTimeMillis(); comp.process(compilerArgs); long stop = System.currentTimeMillis(); System.err.println("Compiler runtime: " + (stop - start) + "ms."); @@ -365,14 +257,17 @@ private static void processRequest(List args) throws Exception { } else { String[] jarCreatorArgs = { "-m", - manifestPath, + ops.manifestPath, outputPath.toString(), tmpPath.toString() }; JarCreator.buildJar(jarCreatorArgs); - if(iJarEnabled) { - Process iostat = new ProcessBuilder().command(ijarCmdPath, outputName, ijarOutput).inheritIO().start(); + if(ops.iJarEnabled) { + Process iostat = new ProcessBuilder() + .command(ops.ijarCmdPath, ops.outputName, ops.ijarOutput) + .inheritIO() + .start(); int exitCode = iostat.waitFor(); if(exitCode != 0) { throw new RuntimeException("ijar process failed!"); @@ -384,34 +279,16 @@ private static void processRequest(List args) throws Exception { } public static void main(String[] args) { - try { -if (ImmutableSet.copyOf(args).contains("--persistent_worker")) { - runPersistentWorker(new WorkerOptions()); - } else { -processRequest(Arrays.asList(args)); -} -} -catch (FileNotFoundException ex){ - throw new RuntimeException("nope", ex); -} -catch (Exception ex){ - throw new RuntimeException("nope", ex); -} - - -// Settings s = new Settings(); - -// Global g = new Global(s); - -// Global.Run run = g.new Run(); - -// // run.compile(List("test.scala")) // invoke compiler. it creates Test.class. - -// for (int i = 0; i < args.length; i++) { -// System.err.println(i); -// } -// System.err.println("Helloooo world!!!"); -// System.exit(-1); -} + if (ImmutableSet.copyOf(args).contains("--persistent_worker")) { + runPersistentWorker(new WorkerOptions()); + } + else { + processRequest(Arrays.asList(args)); + } + } + catch (Exception ex) { + throw new RuntimeException("nope", ex); + } + } }