Repository: google/closure-compiler
Issue: Cannot use globs when running the compiler.

Since 62ca536... I am no longer able to use globs for files when running the compiler:

Running this command used to work before that change:

```
java -jar ../../compiler/compiler.jar \\
--flagfile=options/compile.ini \\
--define "goog.json.USE_NATIVE_JSON=true" --define "goog.ui.Component.ALLOW_DETACHED_DECORATION=true" --define "goog.NATIVE_ARRAY_PROTOTYPES=true" --define "goog.array.ASSUME_NATIVE_FUNCTIONS=true" --define "goog.Promise.UNHANDLED_REJECTION_DELAY=-1"  --define "app.settings.RUN_AS_USER=false" --externs=../externs/material.js --generate_exports --export_local_property_definitions --language_in=ECMASCRIPT6 --language_out=ES3 --use_types_for_optimization --output_wrapper="(function(){%output%})();" \\
--only_closure_dependencies \\
--closure_entry_point=app \\
--manage_closure_dependencies true \\
--output_manifest build/app.filelist.txt \\
--js_output_file /tmp/closure_compiler_build \\
--js="js/**.js" --js="tpl/en/**.js" --js="../../templates/soyutils_usegoog.js" --js="../../library/closure/goog/**.js" --js="../../library/third_party/closure/goog/mochikit/async/deferred.js" --js="../../library/third_party/closure/goog/mochikit/async/deferredlist.js" --js="../pstj/animation/**.js" --js="../pstj/app/**.js" --js="../pstj/autogenerated/**.js" --js="../pstj/cast/**.js" --js="../pstj/color/**.js" --js="../pstj/config/**.js" --js="../pstj/control/**.js" --js="../pstj/date/**.js" --js="../pstj/debug/**.js" --js="../pstj/ds/**.js" --js="../pstj/element/**.js" --js="../pstj/error/**.js" --js="../pstj/fx/**.js" --js="../pstj/graphics/**.js" --js="../pstj/material/**.js" --js="../pstj/math/**.js" --js="../pstj/mvc/**.js" --js="../pstj/ng/**.js" --js="../pstj/object/**.js" --js="../pstj/options/**.js" --js="../pstj/resource/**.js" --js="../pstj/sourcegen/**.js" --js="../pstj/storage/**.js" --js="../pstj/style/**.js" --js="../pstj/themes/**.js" --js="../pstj/ui/**.js" --js="../smjs/ds/**.js" --js="../smjs/element/**.js" --js="../smjs/persistence/**.js" --js="../smjs/player/**.js" --js="../smjs/remotecontrol/**.js" --js="../smjs/transport/**.js" --js="../smjs/tv/**.js" --js="../smjs/widgets/**.js" --js="!**_test.js"
```

Now (after the mentioned change) it gives this:

```
ERROR - Cannot read: !**_test.js

ERROR - Cannot read: ../../library/closure/goog/**.js

ERROR - Cannot read: ../pstj/animation/**.js

ERROR - Cannot read: ../pstj/app/**.js

ERROR - Cannot read: ../pstj/autogenerated/**.js

ERROR - Cannot read: ../pstj/cast/**.js

ERROR - Cannot read: ../pstj/color/**.js

ERROR - Cannot read: ../pstj/config/**.js

ERROR - Cannot read: ../pstj/control/**.js

ERROR - Cannot read: ../pstj/date/**.js

ERROR - Cannot read: ../pstj/debug/**.js

ERROR - Cannot read: ../pstj/ds/**.js

ERROR - Cannot read: ../pstj/element/**.js

ERROR - Cannot read: ../pstj/error/**.js

ERROR - Cannot read: ../pstj/fx/**.js

ERROR - Cannot read: ../pstj/graphics/**.js

ERROR - Cannot read: ../pstj/material/**.js

ERROR - Cannot read: ../pstj/math/**.js

ERROR - Cannot read: ../pstj/mvc/**.js

ERROR - Cannot read: ../pstj/ng/**.js

ERROR - Cannot read: ../pstj/object/**.js

ERROR - Cannot read: ../pstj/options/**.js

ERROR - Cannot read: ../pstj/resource/**.js

ERROR - Cannot read: ../pstj/sourcegen/**.js

ERROR - Cannot read: ../pstj/storage/**.js

ERROR - Cannot read: ../pstj/style/**.js

ERROR - Cannot read: ../pstj/themes/**.js

ERROR - Cannot read: ../pstj/ui/**.js

ERROR - Cannot read: ../smjs/ds/**.js

ERROR - Cannot read: ../smjs/element/**.js

ERROR - Cannot read: ../smjs/persistence/**.js

ERROR - Cannot read: ../smjs/player/**.js

ERROR - Cannot read: ../smjs/remotecontrol/**.js

ERROR - Cannot read: ../smjs/transport/**.js

ERROR - Cannot read: ../smjs/tv/**.js

ERROR - Cannot read: ../smjs/widgets/**.js

ERROR - Cannot read: js/**.js

ERROR - Cannot read: tpl/en/**.js

ERROR - required entry point "app" never provided

39 error(s), 0 warning(s)
```


Changed files:
src/com/google/javascript/jscomp/AbstractCommandLineRunner.java, src/com/google/javascript/jscomp/CommandLineRunner.java, test/com/google/javascript/jscomp/CommandLineRunnerTest.java

diff --git a/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java b/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java
index fbdc565a5..87f9c2037 100644
--- a/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java
+++ b/src/com/google/javascript/jscomp/AbstractCommandLineRunner.java
@@ -2542,6 +2542,16 @@ public abstract class AbstractCommandLineRunner<A extends Compiler,
       this.flag = flag;
       this.value = value;
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof FlagEntry) {
+        FlagEntry<?> that = (FlagEntry<?>) o;
+        return that.flag.equals(this.flag)
+            && that.value.equals(this.value);
+      }
+      return false;
+    }
   }
 
   /**
diff --git a/src/com/google/javascript/jscomp/CommandLineRunner.java b/src/com/google/javascript/jscomp/CommandLineRunner.java
index 47c87615d..1f83037d4 100644
--- a/src/com/google/javascript/jscomp/CommandLineRunner.java
+++ b/src/com/google/javascript/jscomp/CommandLineRunner.java
@@ -61,6 +61,7 @@ import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -745,12 +746,21 @@ public class CommandLineRunner extends
     protected List<FlagEntry<JsSourceType>> getMixedJsSources()
         throws CmdLineException, IOException {
       List<FlagEntry<JsSourceType>> mixedSources = new ArrayList<>();
+      Set<String> excludes = new HashSet<>();
       for (FlagEntry<JsSourceType> source : Flags.mixedJsSources) {
         if (source.value.endsWith(".zip")) {
           mixedSources.add(source);
+        } else if (source.value.startsWith("!")) {
+          for (String filename : findJsFiles(
+              Collections.singletonList(source.value.substring(1)))) {
+            excludes.add(filename);
+            mixedSources.remove(new FlagEntry<>(JsSourceType.JS, filename));
+          }
         } else {
           for (String filename : findJsFiles(Collections.singletonList(source.value))) {
-            mixedSources.add(new FlagEntry<>(JsSourceType.JS, filename));
+            if (!excludes.contains(filename)) {
+              mixedSources.add(new FlagEntry<>(JsSourceType.JS, filename));
+            }
           }
         }
       }
@@ -1475,23 +1485,25 @@ public class CommandLineRunner extends
    */
   public static List<String> findJsFiles(Collection<String> patterns) throws IOException {
     Set<String> allJsInputs = new TreeSet<>();
+    Set<String> excludes = new HashSet<>();
     for (String pattern : patterns) {
       if (!pattern.contains("*") && !pattern.startsWith("!")) {
         File matchedFile = new File(pattern);
         if (matchedFile.isDirectory()) {
-          matchPaths(new File(matchedFile, "**.js").toString(), allJsInputs);
-        } else {
+          matchPaths(new File(matchedFile, "**.js").toString(), allJsInputs, excludes);
+        } else if (!excludes.contains(pattern)) {
           allJsInputs.add(pattern);
         }
       } else {
-        matchPaths(pattern, allJsInputs);
+        matchPaths(pattern, allJsInputs, excludes);
       }
     }
 
     return new ArrayList<>(allJsInputs);
   }
 
-  private static void matchPaths(String pattern, final Set<String> allJsInputs)
+  private static void matchPaths(String pattern, final Set<String> allJsInputs,
+      final Set<String> excludes)
       throws IOException {
     FileSystem fs = FileSystems.getDefault();
     final boolean remove = pattern.indexOf('!') == 0;
@@ -1526,10 +1538,12 @@ public class CommandLineRunner extends
           @Override public FileVisitResult visitFile(
               Path p, BasicFileAttributes attrs) {
             if (matcher.matches(p.normalize())) {
+              String pathString = p.toString();
               if (remove) {
-                allJsInputs.remove(p.toString());
-              } else {
-                allJsInputs.add(p.toString());
+                excludes.add(pathString);
+                allJsInputs.remove(pathString);
+              } else if (!excludes.contains(pathString)) {
+                allJsInputs.add(pathString);
               }
             }
             return FileVisitResult.CONTINUE;
diff --git a/test/com/google/javascript/jscomp/CommandLineRunnerTest.java b/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
index 66b8ec811..0af52d6cb 100644
--- a/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
+++ b/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
@@ -1139,6 +1139,34 @@ public final class CommandLineRunnerTest extends TestCase {
     }
   }
 
+  public void testGlobJs3() throws IOException, FlagUsageException {
+    FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
+    FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
+    new File(jsFile2.value).renameTo(new File(
+        new File(jsFile1.value).getParentFile() + File.separator + "test2.js"));
+    // Make sure test2.js is excluded from the inputs when the exclusion
+    // comes after the inclusion
+    String glob1 = new File(jsFile1.value).getParent() + File.separator + "**.js";
+    String glob2 = "!" + new File(jsFile1.value).getParent() + File.separator + "**test2.js";
+    compileFiles(
+        "var a;", new FlagEntry<>(JsSourceType.JS, glob1),
+        new FlagEntry<>(JsSourceType.JS, glob2));
+  }
+
+  public void testGlobJs4() throws IOException, FlagUsageException {
+    FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
+    FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
+    new File(jsFile2.value).renameTo(new File(
+        new File(jsFile1.value).getParentFile() + File.separator + "test2.js"));
+    // Make sure test2.js is excluded from the inputs when the exclusion
+    // comes before the inclusion
+    String glob1 = "!" + new File(jsFile1.value).getParent() + File.separator + "**test2.js";
+    String glob2 = new File(jsFile1.value).getParent() + File.separator + "**.js";
+    compileFiles(
+        "var a;", new FlagEntry<>(JsSourceType.JS, glob1),
+        new FlagEntry<>(JsSourceType.JS, glob2));
+  }
+
   public void testSourceMapInputs() throws Exception {
     args.add("--js_output_file");
     args.add("/path/to/out.js");