Add the option --experimental_max_directories_to_eagerly_visit_in_globbing. The first legacy glob that a package requires will, if this option is enabled, cause up to that many directories to be eagerly visited by a glob(['**']). The results are thrown away for memory reasons.

--
MOS_MIGRATED_REVID=135148361
diff --git a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
index 694601d..5259385 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/GlobCache.java
@@ -26,7 +26,6 @@
 import com.google.devtools.build.lib.util.Preconditions;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.UnixGlob;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -39,6 +38,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -76,29 +76,37 @@
    * System call caching layer.
    */
   private AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls;
+  private final int maxDirectoriesToEagerlyVisit;
 
   /**
    * The thread pool for glob evaluation.
    */
   private final ThreadPoolExecutor globExecutor;
+  private final AtomicBoolean globalStarted = new AtomicBoolean(false);
 
   /**
    * Create a glob expansion cache.
-   * @param packageDirectory globs will be expanded relatively to this
-   *                         directory.
+   *
+   * @param packageDirectory globs will be expanded relatively to this directory.
    * @param packageId the name of the package this cache belongs to.
    * @param locator the package locator.
    * @param globExecutor thread pool for glob evaluation.
+   * @param maxDirectoriesToEagerlyVisit the number of directories to eagerly traverse on the first
+   *     glob for a given package, in order to warm the filesystem. -1 means do no eager traversal.
+   *     See {@code PackageCacheOptions#maxDirectoriesToEagerlyVisitInGlobbing}.
    */
-  public GlobCache(final Path packageDirectory,
-                   final PackageIdentifier packageId,
-                   final CachingPackageLocator locator,
-                   AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls,
-                   ThreadPoolExecutor globExecutor) {
+  public GlobCache(
+      final Path packageDirectory,
+      final PackageIdentifier packageId,
+      final CachingPackageLocator locator,
+      AtomicReference<? extends UnixGlob.FilesystemCalls> syscalls,
+      ThreadPoolExecutor globExecutor,
+      int maxDirectoriesToEagerlyVisit) {
     this.packageDirectory = Preconditions.checkNotNull(packageDirectory);
     this.packageId = Preconditions.checkNotNull(packageId);
     this.globExecutor = Preconditions.checkNotNull(globExecutor);
     this.syscalls = syscalls == null ? new AtomicReference<>(UnixGlob.DEFAULT_SYSCALLS) : syscalls;
+    this.maxDirectoriesToEagerlyVisit = maxDirectoriesToEagerlyVisit;
 
     Preconditions.checkNotNull(locator);
     childDirectoryPredicate = new Predicate<Path>() {
@@ -129,6 +137,18 @@
       throws BadGlobException {
     Future<List<Path>> cached = globCache.get(Pair.of(pattern, excludeDirs));
     if (cached == null) {
+      if (maxDirectoriesToEagerlyVisit > -1
+          && !globalStarted.getAndSet(true)
+          && !pattern.startsWith("**")) {
+        UnixGlob.forPath(packageDirectory)
+            .setMaxDirectoriesToEagerlyVisit(maxDirectoriesToEagerlyVisit)
+            .addPattern("**")
+            .setExcludeDirectories(true)
+            .setDirectoryFilter(childDirectoryPredicate)
+            .setThreadPool(globExecutor)
+            .setFilesystemCalls(syscalls)
+            .globAsync(true);
+      }
       cached = safeGlobUnsorted(pattern, excludeDirs);
       setGlobPaths(pattern, excludeDirs, cached);
     }