private void invalidateIfProjectBuildFileParserStateChanged(Cell cell) {
    ImmutableMap<String, String> cellEnv = cell.getBuckConfig().getEnvironment();
    Iterable<String> defaultIncludes = new ParserConfig(cell.getBuckConfig()).getDefaultIncludes();

    boolean invalidateCaches = false;
    try (AutoCloseableLock updateLock = cachedStateLock.updateLock()) {
      Iterable<String> expected = cachedIncludes.get(cell.getRoot());

      if (!cellEnv.equals(cachedEnvironment)) {
        // Contents of System.getenv() have changed. Cowardly refuse to accept we'll parse
        // everything the same way.
        invalidateCaches = true;
      } else if (expected == null || !Iterables.elementsEqual(defaultIncludes, expected)) {
        // Someone's changed the default includes. That's almost definitely caused all our lovingly
        // cached data to be enormously wonky.
        invalidateCaches = true;
      }

      if (!invalidateCaches) {
        return;
      }

      try (AutoCloseableLock writeLock = cachedStateLock.writeLock()) {
        cachedEnvironment = cellEnv;
        cachedIncludes.put(cell.getRoot(), defaultIncludes);
      }
    }
    synchronized (this) {
      invalidateAllCaches();
      knownCells.add(cell);
    }
  }
  @Override
  public void invalidateBasedOn(WatchEvent<?> event) throws InterruptedException {
    if (!WatchEvents.isPathChangeEvent(event)) {
      // Non-path change event, likely an overflow due to many change events: invalidate everything.
      LOG.debug("Parser invalidating entire cache on overflow.");

      invalidateAllCaches();
      return;
    }

    Path path = (Path) event.context();

    for (Cell cell : knownCells) {
      try {
        if (isPathCreateOrDeleteEvent(event)) {
          BuildFileTree buildFiles = buildFileTrees.get(cell);

          if (path.endsWith(cell.getBuildFileName())) {
            // If a build file has been added or removed, reconstruct the build file tree.
            buildFileTrees.invalidate(cell);
          }

          // Added or removed files can affect globs, so invalidate the package build file
          // "containing" {@code path} unless its filename matches a temp file pattern.
          if (!isTempFile(cell, path)) {
            invalidateContainingBuildFile(cell, buildFiles, path);
          }

          LOG.verbose("Invalidating dependents for path %s, cache state %s", path, this);
        }
      } catch (ExecutionException | UncheckedExecutionException e) {
        try {
          throw propagate(e);
        } catch (BuildFileParseException bfpe) {
          LOG.warn("Unable to parse already parsed build file.", bfpe);
        }
      }
    }

    invalidatePath(path);
  }