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/CommandLineRunner.java, test/com/google/javascript/jscomp/CommandLineRunnerTest.java

diff --git a/src/com/google/javascript/jscomp/CommandLineRunner.java b/src/com/google/javascript/jscomp/CommandLineRunner.java
index 34c79d686..019b4d62e 100644
--- a/src/com/google/javascript/jscomp/CommandLineRunner.java
+++ b/src/com/google/javascript/jscomp/CommandLineRunner.java
@@ -26,8 +26,6 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.Files;
-import com.google.javascript.jscomp.AbstractCommandLineRunner.FlagEntry;
-import com.google.javascript.jscomp.AbstractCommandLineRunner.JsSourceType;
 import com.google.javascript.jscomp.SourceMap.LocationMapping;
 import com.google.javascript.rhino.TokenStream;
 import com.google.protobuf.TextFormat;
@@ -47,8 +45,8 @@ import org.kohsuke.args4j.spi.StringOptionHandler;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintStream;
 import java.lang.reflect.AnnotatedElement;
@@ -64,10 +62,10 @@ import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.logging.Level;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -732,6 +730,28 @@ public class CommandLineRunner extends
       return allJsInputs;
     }
 
+    protected List<FlagEntry<JsSourceType>> getMixedJsSources()
+        throws CmdLineException, IOException {
+      List<FlagEntry<JsSourceType>> mixedSources = new ArrayList<>();
+      for (FlagEntry<JsSourceType> source : Flags.mixedJsSources) {
+        if (source.value.endsWith(".zip")) {
+          mixedSources.add(source);
+        } else {
+          for (String filename : findJsFiles(Collections.singletonList(source.value))) {
+            mixedSources.add(new FlagEntry<>(JsSourceType.JS, filename));
+          }
+        }
+      }
+      List<String> fromArguments = findJsFiles(arguments);
+      for (String filename : fromArguments) {
+        mixedSources.add(new FlagEntry<>(JsSourceType.JS, filename));
+      }
+      if (!Flags.mixedJsSources.isEmpty() && !arguments.isEmpty() && mixedSources.isEmpty()) {
+        throw new CmdLineException(parser, "No inputs matched");
+      }
+      return mixedSources;
+    }
+
     List<SourceMap.LocationMapping> getSourceMapLocationMappings() throws CmdLineException {
       ImmutableList.Builder<LocationMapping> locationMappings = ImmutableList.builder();
 
@@ -1078,6 +1098,7 @@ public class CommandLineRunner extends
     Flags.mixedJsSources.clear();
 
     List<String> jsFiles = null;
+    List<FlagEntry<JsSourceType>> mixedSources = null;
     List<LocationMapping> mappings = null;
     ImmutableMap<String, String> sourceMapInputs = null;
     try {
@@ -1089,6 +1110,7 @@ public class CommandLineRunner extends
       }
 
       jsFiles = flags.getJsFiles();
+      mixedSources = flags.getMixedJsSources();
       mappings = flags.getSourceMapLocationMappings();
       sourceMapInputs = flags.getSourceMapInputs();
     } catch (CmdLineException e) {
@@ -1207,7 +1229,7 @@ public class CommandLineRunner extends
           .setExterns(flags.externs)
           .setJs(jsFiles)
           .setJsZip(flags.jszip)
-          .setMixedJsSources(Flags.mixedJsSources)
+          .setMixedJsSources(mixedSources)
           .setJsOutputFile(flags.jsOutputFile)
           .setModule(flags.module)
           .setVariableMapOutputFile(flags.variableMapOutputFile)
@@ -1437,7 +1459,7 @@ public class CommandLineRunner extends
    * within the directory and sub-directories.
    */
   public static List<String> findJsFiles(Collection<String> patterns) throws IOException {
-    Set<String> allJsInputs = new LinkedHashSet<>();
+    Set<String> allJsInputs = new TreeSet<>();
     for (String pattern : patterns) {
       if (!pattern.contains("*") && !pattern.startsWith("!")) {
         File matchedFile = new File(pattern);
@@ -1458,7 +1480,9 @@ public class CommandLineRunner extends
       throws IOException {
     FileSystem fs = FileSystems.getDefault();
     final boolean remove = pattern.indexOf('!') == 0;
-    if (remove) pattern = pattern.substring(1);
+    if (remove) {
+      pattern = pattern.substring(1);
+    }
 
     if (File.separator.equals("\\\\")) {
       pattern = pattern.replace('\\\\', '/');
@@ -1473,18 +1497,20 @@ public class CommandLineRunner extends
         if (i == 0) {
           break;
         } else {
-          prefix = Joiner.on("/").join(patternParts.subList(0, i));
-          pattern = Joiner.on("/").join(patternParts.subList(i, patternParts.size()));
+          prefix = Joiner.on(File.separator).join(patternParts.subList(0, i));
+          pattern = Joiner.on(File.separator).join(patternParts.subList(i, patternParts.size()));
         }
       }
     }
 
-    final PathMatcher matcher = fs.getPathMatcher("glob:" + pattern);
+    final PathMatcher matcher = fs.getPathMatcher("glob:" + (prefix.equals(".")
+        ? pattern
+        : prefix + File.separator + pattern));
     java.nio.file.Files.walkFileTree(
         fs.getPath(prefix), new SimpleFileVisitor<Path>() {
           @Override public FileVisitResult visitFile(
               Path p, BasicFileAttributes attrs) {
-            if (matcher.matches(p)) {
+            if (matcher.matches(p.normalize())) {
               if (remove) {
                 allJsInputs.remove(p.toString());
               } else {
diff --git a/test/com/google/javascript/jscomp/CommandLineRunnerTest.java b/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
index fbee0ec4e..09463a96c 100644
--- a/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
+++ b/test/com/google/javascript/jscomp/CommandLineRunnerTest.java
@@ -42,6 +42,7 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -1113,6 +1114,36 @@ public final class CommandLineRunnerTest extends TestCase {
     }
   }
 
+  public void testGlobJs1() throws IOException {
+    try {
+      FlagEntry<JsSourceType> jsFile1 = createJsFile("test1", "var a;");
+      FlagEntry<JsSourceType> jsFile2 = createJsFile("test2", "var b;");
+      // Move test2 to the same directory as test1, also make the filename of test2
+      // lexicographically larger than test1
+      new File(jsFile2.value).renameTo(new File(
+          new File(jsFile1.value).getParentFile() + File.separator + "utest2.js"));
+      String glob = new File(jsFile1.value).getParent() + File.separator + "**.js";
+      compileFiles(
+          "var a;var b;", new FlagEntry<>(JsSourceType.JS, glob));
+    } catch (FlagUsageException e) {
+      fail("Unexpected exception" + e);
+    }
+  }
+
+  public void testGlobJs2() throws IOException {
+    try {
+      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 + "utest2.js"));
+      String glob = new File(jsFile1.value).getParent() + File.separator + "*test*.js";
+      compileFiles(
+          "var a;var b;", new FlagEntry<>(JsSourceType.JS, glob));
+    } catch (FlagUsageException e) {
+      fail("Unexpected exception" + e);
+    }
+  }
+
   public void testSourceMapInputs() throws Exception {
     args.add("--js_output_file");
     args.add("/path/to/out.js");
@@ -1875,7 +1906,8 @@ public final class CommandLineRunnerTest extends TestCase {
 
   private static FlagEntry<JsSourceType> createZipFile(Map<String, String> entryContentsByName)
       throws IOException {
-    File tempZipFile = File.createTempFile("testdata", ".js.zip");
+    File tempZipFile = File.createTempFile("testdata", ".js.zip",
+        Files.createTempDirectory("jscomp").toFile());
 
     try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(tempZipFile))) {
       for (Entry<String, String> entry : entryContentsByName.entrySet()) {
@@ -1889,9 +1921,11 @@ public final class CommandLineRunnerTest extends TestCase {
 
   private FlagEntry<JsSourceType> createJsFile(String filename, String fileContent)
       throws IOException {
-    File tempJsFile = File.createTempFile(filename, ".js");
-    FileOutputStream fileOutputStream = new FileOutputStream(tempJsFile);
-    fileOutputStream.write(fileContent.getBytes(java.nio.charset.StandardCharsets.UTF_8));
+    File tempJsFile = File.createTempFile(filename, ".js",
+        Files.createTempDirectory("jscomp").toFile());
+    try (FileOutputStream fileOutputStream = new FileOutputStream(tempJsFile)) {
+      fileOutputStream.write(fileContent.getBytes(java.nio.charset.StandardCharsets.UTF_8));
+    }
 
     return new FlagEntry<>(JsSourceType.JS, tempJsFile.getAbsolutePath());
   }