private void runBuild(final MessageHandler msgHandler, CanceledStatus cs) throws Throwable {
    final File dataStorageRoot = Utils.getDataStorageRoot(myProjectPath);
    if (dataStorageRoot == null) {
      msgHandler.processMessage(
          new CompilerMessage(
              "build",
              BuildMessage.Kind.ERROR,
              "Cannot determine build data storage root for project " + myProjectPath));
      return;
    }
    if (!dataStorageRoot.exists()) {
      // invoked the very first time for this project. Force full rebuild
      myBuildType = BuildType.PROJECT_REBUILD;
    }

    final DataInputStream fsStateStream = createFSDataStream(dataStorageRoot);

    if (fsStateStream != null) {
      // optimization: check whether we can skip the build
      final boolean hasWorkToDoWithModules = fsStateStream.readBoolean();
      if (myBuildType == BuildType.MAKE
          && !hasWorkToDoWithModules
          && scopeContainsModulesOnly(myBuildRunner.getScopes())
          && !containsChanges(myInitialFSDelta)) {
        updateFsStateOnDisk(dataStorageRoot, fsStateStream, myInitialFSDelta.getOrdinal());
        return;
      }
    }

    final BuildFSState fsState = new BuildFSState(false);
    try {
      final ProjectDescriptor pd = myBuildRunner.load(msgHandler, dataStorageRoot, fsState);
      myProjectDescriptor = pd;
      if (fsStateStream != null) {
        try {
          try {
            fsState.load(fsStateStream, pd.getModel(), pd.getBuildRootIndex());
            applyFSEvent(pd, myInitialFSDelta);
          } finally {
            fsStateStream.close();
          }
        } catch (Throwable e) {
          LOG.error(e);
          fsState.clearAll();
        }
      }
      myLastEventOrdinal = myInitialFSDelta != null ? myInitialFSDelta.getOrdinal() : 0L;

      // free memory
      myInitialFSDelta = null;
      // ensure events from controller are processed after FSState initialization
      myEventsProcessor.startProcessing();

      myBuildRunner.runBuild(pd, cs, myConstantSearch, msgHandler, myBuildType);
    } finally {
      saveData(fsState, dataStorageRoot);
    }
  }
 @Nullable
 private DataInputStream createFSDataStream(File dataStorageRoot) {
   if (myInitialFSDelta == null) {
     // this will force FS rescan
     return null;
   }
   try {
     final File file = new File(dataStorageRoot, FS_STATE_FILE);
     final InputStream fs = new FileInputStream(file);
     byte[] bytes;
     try {
       bytes = FileUtil.loadBytes(fs, (int) file.length());
     } finally {
       fs.close();
     }
     final DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes));
     final int version = in.readInt();
     if (version != FSState.VERSION) {
       return null;
     }
     final long savedOrdinal = in.readLong();
     if (savedOrdinal + 1L != myInitialFSDelta.getOrdinal()) {
       return null;
     }
     return in;
   } catch (FileNotFoundException ignored) {
   } catch (Throwable e) {
     LOG.error(e);
   }
   return null;
 }
  private void applyFSEvent(
      ProjectDescriptor pd, @Nullable CmdlineRemoteProto.Message.ControllerMessage.FSEvent event)
      throws IOException {
    if (event == null) {
      return;
    }

    final Timestamps timestamps = pd.timestamps.getStorage();

    for (String deleted : event.getDeletedPathsList()) {
      final File file = new File(deleted);
      Collection<BuildRootDescriptor> descriptor =
          pd.getBuildRootIndex().findAllParentDescriptors(file, null, null);
      if (!descriptor.isEmpty()) {
        if (Utils.IS_TEST_MODE) {
          LOG.info("Applying deleted path from fs event: " + file.getPath());
        }
        for (BuildRootDescriptor rootDescriptor : descriptor) {
          pd.fsState.registerDeleted(rootDescriptor.getTarget(), file, timestamps);
        }
      } else if (Utils.IS_TEST_MODE) {
        LOG.info("Skipping deleted path: " + file.getPath());
      }
    }
    for (String changed : event.getChangedPathsList()) {
      final File file = new File(changed);
      Collection<BuildRootDescriptor> descriptors =
          pd.getBuildRootIndex().findAllParentDescriptors(file, null, null);
      if (!descriptors.isEmpty()) {
        if (Utils.IS_TEST_MODE) {
          LOG.info("Applying dirty path from fs event: " + file.getPath());
        }
        for (BuildRootDescriptor descriptor : descriptors) {
          pd.fsState.markDirty(null, file, descriptor, timestamps);
        }
      } else if (Utils.IS_TEST_MODE) {
        LOG.info("Skipping dirty path: " + file.getPath());
      }
    }
  }
 private static boolean containsChanges(
     CmdlineRemoteProto.Message.ControllerMessage.FSEvent event) {
   return event.getChangedPathsCount() != 0 || event.getDeletedPathsCount() != 0;
 }