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.Collections;
19 import java.util.List;
20 import java.util.concurrent.Callable;
21 import java.util.concurrent.ExecutorService;
22 import java.util.concurrent.Executors;
23 import java.util.concurrent.Future;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
28 * Prepares and executes several instances of the closure compiler.
31 protected final Flags flags = new Flags();
32 private final PrintStream err;
33 private boolean isConfigValid;
35 public Runner(String[] args, PrintStream err) {
37 List<String> argList = processArgs(args);
38 CmdLineParser parser = new CmdLineParser(flags);
41 parser.parseArgument(argList.toArray(new String[] {}));
42 if (flags.compilerArgsFile == null) {
43 isConfigValid = false;
45 } catch (CmdLineException e) {
46 err.println(e.getMessage());
47 isConfigValid = false;
51 parser.printUsage(err);
55 private List<String> processArgs(String[] args) {
56 Pattern argPattern = Pattern.compile("(--[a-zA-Z_]+)=(.*)");
57 Pattern quotesPattern = Pattern.compile("^['\"](.*)['\"]$");
58 List<String> processedArgs = Lists.newArrayList();
60 for (String arg : args) {
61 Matcher matcher = argPattern.matcher(arg);
62 if (matcher.matches()) {
63 processedArgs.add(matcher.group(1));
65 String value = matcher.group(2);
66 Matcher quotesMatcher = quotesPattern.matcher(value);
67 if (quotesMatcher.matches()) {
68 processedArgs.add(quotesMatcher.group(1));
70 processedArgs.add(value);
73 processedArgs.add(arg);
80 private boolean shouldRunCompiler() {
84 protected void logError(String message, Exception e) {
85 err.println("ERROR: " + message);
87 e.printStackTrace(err);
92 List<CompilerInstanceDescriptor> descriptors = getDescriptors();
93 if (descriptors == null) {
96 ExecutorService executor = Executors.newFixedThreadPool(
97 Math.min(descriptors.size(), Runtime.getRuntime().availableProcessors() / 2 + 1));
99 runWithExecutor(descriptors, executor);
105 private void runWithExecutor(
106 List<CompilerInstanceDescriptor> descriptors, ExecutorService executor) {
107 List<Future<CompilerRunner>> futures = new ArrayList<>(descriptors.size());
108 for (CompilerInstanceDescriptor descriptor : descriptors) {
109 CompilerRunner task = new CompilerRunner(descriptor, new ByteArrayOutputStream(512));
110 futures.add(executor.submit(task));
113 for (Future<CompilerRunner> future : futures) {
115 CompilerRunner task = future.get();
116 int result = task.result;
118 System.err.println("ERROR: Compiler returned " + result);
120 task.errStream.flush();
121 System.err.println("@@ START_MODULE:" + task.descriptor.moduleName + " @@");
122 System.err.println(task.errStream.toString("UTF-8"));
123 System.err.println("@@ END_MODULE @@");
124 } catch (Exception e) {
125 System.err.println("ERROR - " + e.getMessage());
131 private List<CompilerInstanceDescriptor> getDescriptors() {
132 List<CompilerInstanceDescriptor> result = new ArrayList<>();
133 try (BufferedReader reader = new BufferedReader(
134 new InputStreamReader(
135 new FileInputStream(flags.compilerArgsFile), "UTF-8"))) {
139 String line = reader.readLine();
143 if (line.length() == 0) {
146 String[] moduleAndArgs = line.split(" +", 2);
147 if (moduleAndArgs.length != 2) {
148 logError(String.format(
149 "Line %d does not contain module name and compiler arguments",
153 result.add(new CompilerInstanceDescriptor(moduleAndArgs[0], moduleAndArgs[1]));
155 } catch (IOException e) {
156 logError("Failed to read compiler arguments file", e);
163 public static void main(String[] args) {
164 Runner runner = new Runner(args, System.err);
165 if (runner.shouldRunCompiler()) {
172 private static class LocalCommandLineRunner extends CommandLineRunner {
173 protected LocalCommandLineRunner(String[] args, PrintStream out, PrintStream err) {
174 super(args, out, err);
178 protected CompilerOptions createOptions() {
179 CompilerOptions options = super.createOptions();
180 options.setIdeMode(true);
181 options.setExtraAnnotationNames(Collections.singletonList("suppressReceiverCheck"));
186 protected void setRunOptions(CompilerOptions options)
187 throws FlagUsageException, IOException {
188 super.setRunOptions(options);
189 options.setCodingConvention(new DevToolsCodingConvention());
196 for (int i = 0; i < runs && result == 0; i++) {
199 } catch (Throwable t) {
207 private static class CompilerRunner implements Callable<CompilerRunner> {
208 private final CompilerInstanceDescriptor descriptor;
209 private final ByteArrayOutputStream errStream;
212 public CompilerRunner(
213 CompilerInstanceDescriptor descriptor, ByteArrayOutputStream errStream) {
214 this.descriptor = descriptor;
215 this.errStream = errStream;
219 public CompilerRunner call() throws Exception {
220 PrintStream errPrintStream = new PrintStream(errStream, false, "UTF-8");
221 LocalCommandLineRunner runner =
222 new LocalCommandLineRunner(prepareArgs(), System.out, errPrintStream);
223 if (!runner.shouldRunCompiler()) {
226 this.result = runner.execute();
230 private String[] prepareArgs() {
231 // FIXME: This does not support quoted arguments.
232 return descriptor.commandLine.split(" +");
236 private static class Flags {
237 @Option(name = "--compiler-args-file",
238 usage = "Full path to file containing compiler arguments (one line per instance)")
239 private String compilerArgsFile = null;
242 private static class CompilerInstanceDescriptor {
243 private final String moduleName;
244 private final String commandLine;
246 public CompilerInstanceDescriptor(String moduleName, String commandLine) {
247 this.moduleName = moduleName;
248 this.commandLine = commandLine;