/**
   * Preprocess and compile all the code for this sketch.
   *
   * <p>In an advanced program, the returned class name could be different, which is why the
   * className is set based on the return value. A compilation error will burp up a RunnerException.
   *
   * @return null if compilation failed, main class name if not
   */
  public String build(boolean verbose, boolean save)
      throws RunnerException, PreferencesMapException, IOException {
    // run the preprocessor
    editor.status.progressUpdate(20);

    ensureExistence();

    CompilerProgressListener progressListener = editor.status::progressUpdate;

    boolean deleteTemp = false;
    File pathToSketch = sketch.getPrimaryFile().getFile();
    if (sketch.isModified()) {
      // If any files are modified, make a copy of the sketch with the changes
      // saved, so arduino-builder will see the modifications.
      pathToSketch = saveSketchInTempFolder();
      deleteTemp = true;
    }

    try {
      return new Compiler(pathToSketch, sketch).build(progressListener, save);
    } finally {
      // Make sure we clean up any temporary sketch copy
      if (deleteTemp) FileUtils.recursiveDelete(pathToSketch.getParentFile());
    }
  }
  /**
   * Called whenever the modification status of one of the tabs changes. TODO: Move this code into
   * Editor and improve decoupling from EditorTab
   */
  public void calcModified() {
    editor.header.repaint();

    if (OSUtils.isMacOS()) {
      // http://developer.apple.com/qa/qa2001/qa1146.html
      Object modifiedParam = sketch.isModified() ? Boolean.TRUE : Boolean.FALSE;
      editor.getRootPane().putClientProperty("windowModified", modifiedParam);
      editor.getRootPane().putClientProperty("Window.documentModified", modifiedParam);
    }
  }