Index: third_party/closure_compiler/runner/src/org/chromium/closure/compiler/Runner.java |
diff --git a/third_party/closure_compiler/runner/src/org/chromium/closure/compiler/Runner.java b/third_party/closure_compiler/runner/src/org/chromium/closure/compiler/Runner.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c94913127e0ed7fd0ae126f6f7a5980df08d094d |
--- /dev/null |
+++ b/third_party/closure_compiler/runner/src/org/chromium/closure/compiler/Runner.java |
@@ -0,0 +1,251 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.closure.compiler; |
+ |
+import com.google.common.collect.Lists; |
+import com.google.javascript.jscomp.CommandLineRunner; |
+import com.google.javascript.jscomp.CompilerOptions; |
+ |
+import org.kohsuke.args4j.CmdLineException; |
+import org.kohsuke.args4j.CmdLineParser; |
+import org.kohsuke.args4j.Option; |
+ |
+import java.io.BufferedReader; |
+import java.io.ByteArrayOutputStream; |
+import java.io.FileInputStream; |
+import java.io.IOException; |
+import java.io.InputStreamReader; |
+import java.io.PrintStream; |
+import java.util.ArrayList; |
+import java.util.Collections; |
+import java.util.List; |
+import java.util.concurrent.Callable; |
+import java.util.concurrent.ExecutorService; |
+import java.util.concurrent.Executors; |
+import java.util.concurrent.Future; |
+import java.util.regex.Matcher; |
+import java.util.regex.Pattern; |
+ |
+/** |
+ * Prepares and executes several instances of the closure compiler. |
+ */ |
+public class Runner { |
+ protected final Flags flags = new Flags(); |
+ private final PrintStream err; |
+ private boolean isConfigValid; |
+ |
+ public Runner(String[] args, PrintStream err) { |
+ this.err = err; |
+ List<String> argList = processArgs(args); |
+ CmdLineParser parser = new CmdLineParser(flags); |
+ isConfigValid = true; |
+ try { |
+ parser.parseArgument(argList.toArray(new String[] {})); |
+ if (flags.compilerArgsFile == null) { |
+ isConfigValid = false; |
+ } |
+ } catch (CmdLineException e) { |
+ err.println(e.getMessage()); |
+ isConfigValid = false; |
+ } |
+ |
+ if (!isConfigValid) { |
+ parser.printUsage(err); |
+ } |
+ } |
+ |
+ private List<String> processArgs(String[] args) { |
+ Pattern argPattern = Pattern.compile("(--[a-zA-Z_]+)=(.*)"); |
+ Pattern quotesPattern = Pattern.compile("^['\"](.*)['\"]$"); |
+ List<String> processedArgs = Lists.newArrayList(); |
+ |
+ for (String arg : args) { |
+ Matcher matcher = argPattern.matcher(arg); |
+ if (matcher.matches()) { |
+ processedArgs.add(matcher.group(1)); |
+ |
+ String value = matcher.group(2); |
+ Matcher quotesMatcher = quotesPattern.matcher(value); |
+ if (quotesMatcher.matches()) { |
+ processedArgs.add(quotesMatcher.group(1)); |
+ } else { |
+ processedArgs.add(value); |
+ } |
+ } else { |
+ processedArgs.add(arg); |
+ } |
+ } |
+ |
+ return processedArgs; |
+ } |
+ |
+ private boolean shouldRunCompiler() { |
+ return isConfigValid; |
+ } |
+ |
+ protected void logError(String message, Exception e) { |
+ err.println("ERROR: " + message); |
+ if (e != null) { |
+ e.printStackTrace(err); |
+ } |
+ } |
+ |
+ private void run() { |
+ List<CompilerInstanceDescriptor> descriptors = getDescriptors(); |
+ if (descriptors == null) { |
+ return; |
+ } |
+ ExecutorService executor = Executors.newFixedThreadPool( |
+ Math.min(descriptors.size(), Runtime.getRuntime().availableProcessors() / 2 + 1)); |
+ try { |
+ runWithExecutor(descriptors, executor); |
+ } finally { |
+ executor.shutdown(); |
+ } |
+ } |
+ |
+ private void runWithExecutor( |
+ List<CompilerInstanceDescriptor> descriptors, ExecutorService executor) { |
+ List<Future<CompilerRunner>> futures = new ArrayList<>(descriptors.size()); |
+ for (CompilerInstanceDescriptor descriptor : descriptors) { |
+ CompilerRunner task = new CompilerRunner(descriptor, new ByteArrayOutputStream(512)); |
+ futures.add(executor.submit(task)); |
+ } |
+ |
+ for (Future<CompilerRunner> future : futures) { |
+ try { |
+ CompilerRunner task = future.get(); |
+ |
+ task.errStream.flush(); |
+ |
+ //System.err.println("@@ START_MODULE:" + task.descriptor.moduleName + " @@"); |
+ System.err.println(task.errStream.toString("UTF-8")); |
+ //System.err.println("@@ END_MODULE @@"); |
+ |
+ if (task.result != 0) { |
+ System.exit(task.result); |
+ } |
+ } catch (Exception e) { |
+ System.err.println("ERROR: " + e.getMessage()); |
+ System.exit(1); |
+ } |
+ } |
+ System.exit(0); |
+ } |
+ |
+ private List<CompilerInstanceDescriptor> getDescriptors() { |
+ List<CompilerInstanceDescriptor> result = new ArrayList<>(); |
+ try (BufferedReader reader = new BufferedReader( |
+ new InputStreamReader( |
+ new FileInputStream(flags.compilerArgsFile), "UTF-8"))) { |
+ int lineIndex = 0; |
+ while (true) { |
+ ++lineIndex; |
+ String line = reader.readLine(); |
+ if (line == null) { |
+ break; |
+ } |
+ if (line.length() == 0) { |
+ continue; |
+ } |
+ String[] moduleAndArgs = line.split(" +", 2); |
+ if (moduleAndArgs.length != 2) { |
+ logError(String.format( |
+ "Line %d does not contain module name and compiler arguments", |
+ lineIndex), null); |
+ continue; |
+ } |
+ result.add(new CompilerInstanceDescriptor(moduleAndArgs[0], moduleAndArgs[1])); |
+ } |
+ } catch (IOException e) { |
+ logError("Failed to read compiler arguments file", e); |
+ return null; |
+ } |
+ |
+ return result; |
+ } |
+ |
+ public static void main(String[] args) { |
+ Runner runner = new Runner(args, System.err); |
+ if (runner.shouldRunCompiler()) { |
+ runner.run(); |
+ } else { |
+ System.exit(-1); |
+ } |
+ } |
+ |
+ private static class LocalCommandLineRunner extends CommandLineRunner { |
+ protected LocalCommandLineRunner(String[] args, PrintStream out, PrintStream err) { |
+ super(args, out, err); |
+ } |
+ |
+ @Override |
+ protected CompilerOptions createOptions() { |
+ CompilerOptions options = super.createOptions(); |
+ options.setIdeMode(true); |
+ options.setExtraAnnotationNames(Collections.singletonList("suppressReceiverCheck")); |
+ return options; |
+ } |
+ |
+ int execute() { |
+ int result = 0; |
+ int runs = 1; |
+ try { |
+ for (int i = 0; i < runs && result == 0; i++) { |
+ result = doRun(); |
+ } |
+ } catch (Throwable t) { |
+ t.printStackTrace(); |
+ result = -2; |
+ } |
+ return result; |
+ } |
+ } |
+ |
+ private static class CompilerRunner implements Callable<CompilerRunner> { |
+ private final CompilerInstanceDescriptor descriptor; |
+ private final ByteArrayOutputStream errStream; |
+ private int result; |
+ |
+ public CompilerRunner( |
+ CompilerInstanceDescriptor descriptor, ByteArrayOutputStream errStream) { |
+ this.descriptor = descriptor; |
+ this.errStream = errStream; |
+ } |
+ |
+ @Override |
+ public CompilerRunner call() throws Exception { |
+ PrintStream errPrintStream = new PrintStream(errStream, false, "UTF-8"); |
+ LocalCommandLineRunner runner = |
+ new LocalCommandLineRunner(prepareArgs(), System.out, errPrintStream); |
+ if (!runner.shouldRunCompiler()) { |
+ this.result = -1; |
+ } |
+ this.result = runner.execute(); |
+ return this; |
+ } |
+ |
+ private String[] prepareArgs() { |
+ // FIXME: This does not support quoted arguments. |
+ return descriptor.commandLine.split(" +"); |
+ } |
+ } |
+ |
+ private static class Flags { |
+ @Option(name = "--compiler-args-file", |
+ usage = "Full path to file containing compiler arguments (one line per instance)") |
+ private String compilerArgsFile = null; |
+ } |
+ |
+ private static class CompilerInstanceDescriptor { |
+ private final String moduleName; |
+ private final String commandLine; |
+ |
+ public CompilerInstanceDescriptor(String moduleName, String commandLine) { |
+ this.moduleName = moduleName; |
+ this.commandLine = commandLine; |
+ } |
+ } |
+} |