1 package org.chromium.devtools.compiler;
3 import com.google.common.collect.Lists;
4 import com.google.javascript.jscomp.CommandLineRunner;
5 import com.google.javascript.jscomp.CompilerOptions;
7 import org.kohsuke.args4j.CmdLineException;
8 import org.kohsuke.args4j.CmdLineParser;
9 import org.kohsuke.args4j.Option;
11 import java.io.BufferedReader;
12 import java.io.ByteArrayOutputStream;
13 import java.io.FileInputStream;
14 import java.io.IOException;
15 import java.io.InputStreamReader;
16 import java.io.PrintStream;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.Callable;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.Future;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
27 * Prepares and executes several instances of the closure compiler.
30 protected final Flags flags = new Flags();
31 private final PrintStream err;
32 private boolean isConfigValid;
34 public Runner(String[] args, PrintStream err) {
36 List<String> argList = processArgs(args);
37 CmdLineParser parser = new CmdLineParser(flags);
40 parser.parseArgument(argList.toArray(new String[] {}));
41 if (flags.compilerArgsFile == null) {
42 isConfigValid = false;
44 } catch (CmdLineException e) {
45 err.println(e.getMessage());
46 isConfigValid = false;
50 parser.printUsage(err);
54 private List<String> processArgs(String[] args) {
55 Pattern argPattern = Pattern.compile("(--[a-zA-Z_]+)=(.*)");
56 Pattern quotesPattern = Pattern.compile("^['\"](.*)['\"]$");
57 List<String> processedArgs = Lists.newArrayList();
59 for (String arg : args) {
60 Matcher matcher = argPattern.matcher(arg);
61 if (matcher.matches()) {
62 processedArgs.add(matcher.group(1));
64 String value = matcher.group(2);
65 Matcher quotesMatcher = quotesPattern.matcher(value);
66 if (quotesMatcher.matches()) {
67 processedArgs.add(quotesMatcher.group(1));
69 processedArgs.add(value);
72 processedArgs.add(arg);
79 private boolean shouldRunCompiler() {
83 protected void logError(String message, Exception e) {
84 err.println("ERROR: " + message);
86 e.printStackTrace(err);
91 List<CompilerInstanceDescriptor> descriptors = getDescriptors();
92 if (descriptors == null) {
95 ExecutorService executor = Executors.newFixedThreadPool(
96 Math.min(descriptors.size(), Runtime.getRuntime().availableProcessors() / 2 + 1));
98 runWithExecutor(descriptors, executor);
104 private void runWithExecutor(
105 List<CompilerInstanceDescriptor> descriptors, ExecutorService executor) {
106 List<Future<CompilerRunner>> futures = new ArrayList<>(descriptors.size());
107 for (CompilerInstanceDescriptor descriptor : descriptors) {
108 CompilerRunner task = new CompilerRunner(descriptor, new ByteArrayOutputStream(512));
109 futures.add(executor.submit(task));
112 for (Future<CompilerRunner> future : futures) {
114 CompilerRunner task = future.get();
115 int result = task.result;
117 System.err.println("ERROR: Compiler returned " + result);
119 task.errStream.flush();
120 System.err.println("@@ START_MODULE:" + task.descriptor.moduleName + " @@");
121 System.err.println(task.errStream.toString("UTF-8"));
122 System.err.println("@@ END_MODULE @@");
123 } catch (Exception e) {
124 System.err.println("ERROR - " + e.getMessage());
130 private List<CompilerInstanceDescriptor> getDescriptors() {
131 List<CompilerInstanceDescriptor> result = new ArrayList<>();
132 try (BufferedReader reader = new BufferedReader(
133 new InputStreamReader(
134 new FileInputStream(flags.compilerArgsFile), "UTF-8"))) {
138 String line = reader.readLine();
142 if (line.length() == 0) {
145 String[] moduleAndArgs = line.split(" +", 2);
146 if (moduleAndArgs.length != 2) {
147 logError(String.format(
148 "Line %d does not contain module name and compiler arguments",
152 result.add(new CompilerInstanceDescriptor(moduleAndArgs[0], moduleAndArgs[1]));
154 } catch (IOException e) {
155 logError("Failed to read compiler arguments file", e);
162 public static void main(String[] args) {
163 Runner runner = new Runner(args, System.err);
164 if (runner.shouldRunCompiler()) {
171 private static class LocalCommandLineRunner extends CommandLineRunner {
172 protected LocalCommandLineRunner(String[] args, PrintStream out, PrintStream err) {
173 super(args, out, err);
177 protected CompilerOptions createOptions() {
178 CompilerOptions options = super.createOptions();
179 options.setIdeMode(true);
187 for (int i = 0; i < runs && result == 0; i++) {
190 } catch (Throwable t) {
198 private static class CompilerRunner implements Callable<CompilerRunner> {
199 private final CompilerInstanceDescriptor descriptor;
200 private final ByteArrayOutputStream errStream;
203 public CompilerRunner(
204 CompilerInstanceDescriptor descriptor, ByteArrayOutputStream errStream) {
205 this.descriptor = descriptor;
206 this.errStream = errStream;
210 public CompilerRunner call() throws Exception {
211 PrintStream errPrintStream = new PrintStream(errStream, false, "UTF-8");
212 LocalCommandLineRunner runner =
213 new LocalCommandLineRunner(prepareArgs(), System.out, errPrintStream);
214 if (!runner.shouldRunCompiler()) {
217 this.result = runner.execute();
221 private String[] prepareArgs() {
222 // FIXME: This does not support quoted arguments.
223 return descriptor.commandLine.split(" +");
227 private static class Flags {
228 @Option(name = "--compiler-args-file",
229 usage = "Full path to file containing compiler arguments (one line per instance)")
230 private String compilerArgsFile = null;
233 private static class CompilerInstanceDescriptor {
234 private final String moduleName;
235 private final String commandLine;
237 public CompilerInstanceDescriptor(String moduleName, String commandLine) {
238 this.moduleName = moduleName;
239 this.commandLine = commandLine;