diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 83a4a8a113d..3eb60f3d376 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -153,6 +153,8 @@ public class Editor extends JFrame implements RunnerListener { Runnable runHandler; Runnable presentHandler; + Runnable runAndSaveHandler; + Runnable presentAndSaveHandler; Runnable stopHandler; Runnable exportHandler; Runnable exportAppHandler; @@ -557,21 +559,6 @@ public void actionPerformed(ActionEvent e) { }); fileMenu.add(saveAsMenuItem); - item = newJMenuItem(_("Upload"), 'U'); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleExport(false); - } - }); - fileMenu.add(item); - - item = newJMenuItemShift(_("Upload Using Programmer"), 'U'); - item.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - handleExport(true); - } - }); - fileMenu.add(item); fileMenu.addSeparator(); @@ -620,7 +607,7 @@ public void actionPerformed(ActionEvent e) { protected JMenu buildSketchMenu() { JMenuItem item; sketchMenu = new JMenu(_("Sketch")); - + item = newJMenuItem(_("Verify / Compile"), 'R'); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -629,13 +616,29 @@ public void actionPerformed(ActionEvent e) { }); sketchMenu.add(item); -// item = newJMenuItemShift("Verify / Compile (verbose)", 'R'); -// item.addActionListener(new ActionListener() { -// public void actionPerformed(ActionEvent e) { -// handleRun(true); -// } -// }); -// sketchMenu.add(item); + item = newJMenuItemShift("Compile and Save Hex", 'R'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleRunAndSave(false); + } + }); + sketchMenu.add(item); + + item = newJMenuItem(_("Upload"), 'U'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleExport(false); + } + }); + sketchMenu.add(item); + + item = newJMenuItemShift(_("Upload Using Programmer"), 'U'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleExport(true); + } + }); + sketchMenu.add(item); // item = new JMenuItem("Stop"); // item.addActionListener(new ActionListener() { @@ -1414,11 +1417,17 @@ protected void updateRedoState() { // abstract from the editor in this fashion. - public void setHandlers(Runnable runHandler, Runnable presentHandler, + public void setHandlers(Runnable runHandler, + Runnable presentHandler, + Runnable runAndSaveHandler, + Runnable presentAndSaveHandler, Runnable stopHandler, - Runnable exportHandler, Runnable exportAppHandler) { + Runnable exportHandler, + Runnable exportAppHandler) { this.runHandler = runHandler; this.presentHandler = presentHandler; + this.runAndSaveHandler = runHandler; + this.presentAndSaveHandler = presentHandler; this.stopHandler = stopHandler; this.exportHandler = exportHandler; this.exportAppHandler = exportAppHandler; @@ -1428,6 +1437,8 @@ public void setHandlers(Runnable runHandler, Runnable presentHandler, public void resetHandlers() { runHandler = new DefaultRunHandler(); presentHandler = new DefaultPresentHandler(); + runAndSaveHandler = new DefaultRunAndSaveHandler(); + presentAndSaveHandler = new DefaultPresentAndSaveHandler(); stopHandler = new DefaultStopHandler(); exportHandler = new DefaultExportHandler(); exportAppHandler = new DefaultExportAppHandler(); @@ -1916,13 +1927,35 @@ public void handleRun(final boolean verbose) { // placed on the event thread and causes a hang--bad idea all around. new Thread(verbose ? presentHandler : runHandler).start(); } + /** + * Implements Sketch → Run and Save Hex. + * @param verbose Set true to run with verbose output. + */ + public void handleRunAndSave(final boolean verbose) { + internalCloseRunner(); + running = true; + toolbar.activate(EditorToolbar.RUN); + status.progress(_("Compiling sketch...")); + + // do this to advance/clear the terminal window / dos prompt / etc + for (int i = 0; i < 10; i++) System.out.println(); + + // clear the console on each run, unless the user doesn't want to + if (Preferences.getBoolean("console.auto_clear")) { + console.clear(); + } + + // Cannot use invokeLater() here, otherwise it gets + // placed on the event thread and causes a hang--bad idea all around. + new Thread(verbose ? presentAndSaveHandler : runAndSaveHandler).start(); + } // DAM: in Arduino, this is compile class DefaultRunHandler implements Runnable { public void run() { try { sketch.prepare(); - sketch.build(false); + sketch.build(false, false); statusNotice(_("Done compiling.")); } catch (Exception e) { status.unprogress(); @@ -1939,7 +1972,7 @@ class DefaultPresentHandler implements Runnable { public void run() { try { sketch.prepare(); - sketch.build(true); + sketch.build(true, false); statusNotice(_("Done compiling.")); } catch (Exception e) { status.unprogress(); @@ -1950,6 +1983,39 @@ public void run() { toolbar.deactivate(EditorToolbar.RUN); } } +//DAM: in Arduino, this is compile + class DefaultRunAndSaveHandler implements Runnable { + public void run() { + try { + sketch.prepare(); + sketch.build(false, true); + statusNotice(_("Done compiling.")); + } catch (Exception e) { + status.unprogress(); + statusError(e); + } + + status.unprogress(); + toolbar.deactivate(EditorToolbar.RUN); + } + } + + // DAM: in Arduino, this is compile (with verbose output) + class DefaultPresentAndSaveHandler implements Runnable { + public void run() { + try { + sketch.prepare(); + sketch.build(true, true); + statusNotice(_("Done compiling.")); + } catch (Exception e) { + status.unprogress(); + statusError(e); + } + + status.unprogress(); + toolbar.deactivate(EditorToolbar.RUN); + } + } class DefaultStopHandler implements Runnable { public void run() { diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 285962755e9..23b2ab68031 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -1129,8 +1129,8 @@ public void prepare() throws IOException { * @return null if compilation failed, main class name if not * @throws RunnerException */ - public String build(boolean verbose) throws RunnerException { - return build(tempBuildFolder.getAbsolutePath(), verbose); + public String build(boolean verbose, boolean save) throws RunnerException { + return build(tempBuildFolder.getAbsolutePath(), verbose, save); } /** @@ -1142,7 +1142,7 @@ public String build(boolean verbose) throws RunnerException { * * @return null if compilation failed, main class name if not */ - public String build(String buildPath, boolean verbose) throws RunnerException { + public String build(String buildPath, boolean verbose, boolean save) throws RunnerException { // run the preprocessor editor.status.progressUpdate(20); @@ -1155,7 +1155,7 @@ public void progress(int percent) { } }; - return Compiler.build(data, buildPath, tempBuildFolder, pl, verbose); + return Compiler.build(data, buildPath, tempBuildFolder, pl, verbose, save); } protected boolean exportApplet(boolean usingProgrammer) throws Exception { @@ -1173,7 +1173,7 @@ public boolean exportApplet(String appletPath, boolean usingProgrammer) // build the sketch editor.status.progressNotice(_("Compiling sketch...")); - String foundName = build(appletPath, false); + String foundName = build(appletPath, false, false); // (already reported) error during export, exit this function if (foundName == null) return false; diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index 2088bc5f182..184a733b877 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -70,6 +70,7 @@ public class Compiler implements MessageConsumer { private SketchData sketch; private PreferencesMap prefs; private boolean verbose; + private boolean saveHex; private List objectFiles; @@ -86,7 +87,7 @@ public interface ProgressListener { private ProgressListener progressListener; - static public String build(SketchData data, String buildPath, File tempBuildFolder, ProgressListener progListener, boolean verbose) throws RunnerException { + static public String build(SketchData data, String buildPath, File tempBuildFolder, ProgressListener progListener, boolean verbose, boolean save) throws RunnerException { if (SketchData.checkSketchFile(data.getPrimaryFile()) == null) BaseNoGui.showError(_("Bad file selected"), _("Bad sketch primary file or bad sketck directory structure"), null); @@ -115,7 +116,7 @@ static public String build(SketchData data, String buildPath, File tempBuildFold // compile the program. errors will happen as a RunnerException // that will bubble up to whomever called build(). - if (compiler.compile(verbose)) { + if (compiler.compile(verbose, save)) { compiler.size(compiler.getBuildPreferences()); return primaryClassName; } @@ -343,10 +344,11 @@ protected void size(PreferencesMap prefs) throws RunnerException { * @return true if successful. * @throws RunnerException Only if there's a problem. Only then. */ - public boolean compile(boolean _verbose) throws RunnerException { + public boolean compile(boolean _verbose, boolean _save) throws RunnerException { preprocess(prefs.get("build.path")); verbose = _verbose || PreferencesData.getBoolean("build.verbose"); + saveHex = _save; sketchIsCompiled = false; objectFiles = new ArrayList(); @@ -409,6 +411,12 @@ public boolean compile(boolean _verbose) throws RunnerException { // 6. build the .hex file progressListener.progress(80); compileHex(); + + // 7. Save the .hex file + if (saveHex) { + progressListener.progress(85); + saveHex(); + } progressListener.progress(90); return true; @@ -1101,6 +1109,23 @@ void compileHex() throws RunnerException { } execAsynchronously(cmdArray); } + + // 7. Save the .hex file + void saveHex() throws RunnerException { + PreferencesMap dict = new PreferencesMap(prefs); + dict.put("ide_version", "" + BaseNoGui.REVISION); + + String[] cmdArray; + try { + String hexPattern = prefs.get("recipe.hex.pattern"); + String savePath = sketch.getFolder().getAbsolutePath() + "/" + hexPattern; + String cmd = hexPattern + " " + savePath; + cmdArray = StringReplacer.formatAndSplit(cmd, dict, true); + } catch (Exception e) { + throw new RunnerException(e); + } + execAsynchronously(cmdArray); + } private static String prepareIncludes(List includeFolders) { String res = ""; diff --git a/hardware/arduino/avr/platform.txt b/hardware/arduino/avr/platform.txt index 834774614ef..3ce4d6096c6 100644 --- a/hardware/arduino/avr/platform.txt +++ b/hardware/arduino/avr/platform.txt @@ -67,6 +67,10 @@ recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.obj ## Create hex recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex" +## Save hex +recipe.hex.pattern=cp -f "{build.path}/{build.project_name}.hex" +recipe.hex.pattern.windows=copy /y "{build.path}/{build.project_name}.hex" + ## Compute size recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).* diff --git a/hardware/arduino/sam/platform.txt b/hardware/arduino/sam/platform.txt index 85522faf4d0..1b4f16e2a67 100644 --- a/hardware/arduino/sam/platform.txt +++ b/hardware/arduino/sam/platform.txt @@ -71,6 +71,10 @@ recipe.objcopy.eep.pattern= ## Create hex recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin" +## Save hex +recipe.hex.pattern=cp -f "{build.path}/{build.project_name}.bin" +recipe.hex.pattern.windows=copy /y "{build.path}/{build.project_name}.bin" + ## Compute size recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf" recipe.size.regex=\.text\s+([0-9]+).*