diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 184eedc94..700d3b056 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -1454,7 +1454,6 @@ public class Base { Action subAction = new AbstractAction(_(boardCustomMenu.get(customMenuOption))) { public void actionPerformed(ActionEvent e) { Preferences.set("custom_" + menuId, ((TargetBoard)getValue("board")).getId() + "_" + getValue("custom_menu_option")); - Sketch.buildSettingChanged(); } }; subAction.putValue("board", board); @@ -1568,7 +1567,6 @@ public class Base { filterVisibilityOfSubsequentBoardMenus(targetBoard, 1); onBoardOrPortChange(); - Sketch.buildSettingChanged(); rebuildImportMenu(Editor.importMenu); rebuildExamplesMenu(Editor.examplesMenu); } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index a6b6442ee..278b54840 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -2007,8 +2007,6 @@ public class Editor extends JFrame implements RunnerListener { try { stopHandler.run(); } catch (Exception e) { } - - sketch.cleanup(); } diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 95feb0cb5..c8a2967ec 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -31,6 +31,7 @@ import processing.app.debug.*; import processing.app.debug.Compiler; import processing.app.forms.PasswordAuthorizationDialog; import processing.app.helpers.PreferencesMap; +import processing.app.helpers.FileUtils; import processing.app.packages.Library; import processing.app.packages.LibraryList; import processing.app.preproc.*; @@ -96,6 +97,12 @@ public class Sketch { */ private LibraryList importedLibraries; + /** + * File inside the build directory that contains the build options + * used for the last build. + */ + static final String BUILD_PREFS_FILE = "buildprefs.txt"; + /** * path is location of the main .pde file, because this is also * simplest to use when opening the file from the finder/explorer. @@ -1182,12 +1189,12 @@ public class Sketch { /** * Cleanup temporary files used during a build/run. */ - protected void cleanup() { + protected void cleanup(boolean force) { // if the java runtime is holding onto any files in the build dir, we // won't be able to delete them, so we need to force a gc here System.gc(); - if (deleteFilesOnNextBuild) { + if (force) { // delete the entire directory and all contents // when we know something changed and all objects // need to be recompiled, or if the board does not @@ -1199,8 +1206,6 @@ public class Sketch { // work because the build dir won't exist at startup, so the classloader // will ignore the fact that that dir is in the CLASSPATH in run.sh Base.removeDescendants(tempBuildFolder); - - deleteFilesOnNextBuild = false; } else { // delete only stale source files, from the previously // compiled sketch. This allows multiple windows to be @@ -1251,13 +1256,6 @@ public class Sketch { */ //protected String compile() throws RunnerException { - // called when any setting changes that requires all files to be recompiled - public static void buildSettingChanged() { - deleteFilesOnNextBuild = true; - } - - private static boolean deleteFilesOnNextBuild = true; - /** * When running from the editor, take care of preparations before running * the build. @@ -1286,12 +1284,6 @@ public class Sketch { load(); } - // in case there were any boogers left behind - // do this here instead of after exiting, since the exit - // can happen so many different ways.. and this will be - // better connected to the dataFolder stuff below. - cleanup(); - // // handle preprocessing the main file's code // return build(tempBuildFolder.getAbsolutePath()); } @@ -1518,6 +1510,46 @@ public class Sketch { return build(tempBuildFolder.getAbsolutePath(), verbose); } + /** + * Check if the build preferences used on the previous build in + * buildPath match the ones given. + */ + protected boolean buildPreferencesChanged(File buildPrefsFile, String newBuildPrefs) { + // No previous build, so no match + if (!buildPrefsFile.exists()) + return true; + + String previousPrefs; + try { + previousPrefs = FileUtils.readFileToString(buildPrefsFile); + } catch (IOException e) { + System.err.println(_("Could not read prevous build preferences file, rebuilding all")); + return true; + } + + if (!previousPrefs.equals(newBuildPrefs)) { + System.out.println(_("Build options changed, rebuilding all")); + return true; + } else { + return false; + } + } + + /** + * Returns the build preferences of the given compiler as a string. + * Only includes build-specific preferences, to make sure unrelated + * preferences don't cause a rebuild (in particular preferences that + * change on every start, like last.ide.xxx.daterun). */ + protected String buildPrefsString(Compiler compiler) { + PreferencesMap buildPrefs = compiler.getBuildPreferences(); + String res = ""; + SortedSet treeSet = new TreeSet(buildPrefs.keySet()); + for (String k : treeSet) { + if (k.startsWith("build.") || k.startsWith("compiler.") || k.startsWith("recipes.")) + res += k + " = " + buildPrefs.get(k) + "\n"; + } + return res; + } /** * Preprocess and compile all the code for this sketch. @@ -1529,14 +1561,32 @@ public class Sketch { * @return null if compilation failed, main class name if not */ public String build(String buildPath, boolean verbose) throws RunnerException { + String primaryClassName = name + ".cpp"; + Compiler compiler = new Compiler(this, buildPath, primaryClassName); + File buildPrefsFile = new File(buildPath, BUILD_PREFS_FILE); + String newBuildPrefs = buildPrefsString(compiler); + + // Do a forced cleanup (throw everything away) if the previous + // build settings do not match the previous ones + boolean prefsChanged = buildPreferencesChanged(buildPrefsFile, newBuildPrefs); + cleanup(prefsChanged); + + if (prefsChanged) { + try { + PrintWriter out = new PrintWriter(buildPrefsFile); + out.print(newBuildPrefs); + out.close(); + } catch (IOException e) { + System.err.println(_("Could not write build preferences file")); + } + } + // run the preprocessor editor.status.progressUpdate(20); - String primaryClassName = name + ".cpp"; preprocess(buildPath); // compile the program. errors will happen as a RunnerException // that will bubble up to whomever called build(). - Compiler compiler = new Compiler(this, buildPath, primaryClassName); if (compiler.compile(verbose)) { size(compiler.getBuildPreferences()); return primaryClassName;