/**
   * 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());
    }
  }
  private File saveSketchInTempFolder() throws IOException {
    File tempFolder = FileUtils.createTempFolder("arduino_modified_sketch_");
    FileUtils.copy(sketch.getFolder(), tempFolder);

    for (SketchFile file :
        Stream.of(sketch.getFiles()).filter(SketchFile::isModified).collect(Collectors.toList())) {
      Files.write(
          Paths.get(tempFolder.getAbsolutePath(), file.getFileName()),
          file.getProgram().getBytes());
    }

    return Paths.get(tempFolder.getAbsolutePath(), sketch.getPrimaryFile().getFileName()).toFile();
  }