diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index c211ef5f9..09392a0a4 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -152,6 +152,8 @@ public class Editor extends JFrame implements RunnerListener { Runnable runHandler; Runnable presentHandler; + Runnable runAndSaveHandler; + Runnable presentAndSaveHandler; Runnable stopHandler; Runnable exportHandler; Runnable exportAppHandler; @@ -563,22 +565,6 @@ public class Editor extends JFrame implements RunnerListener { }); 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(); item = newJMenuItemShift(_("Page Setup"), 'P'); @@ -637,14 +623,31 @@ public class Editor extends JFrame implements RunnerListener { } }); sketchMenu.add(item); + + item = newJMenuItem(_("Upload"), 'U'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleExport(false); + } + }); + 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(_("Upload Using Programmer"), 'U'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleExport(true); + } + }); + sketchMenu.add(item); + + + item = newJMenuItemAlt("Export compiled Binary", 'S'); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleRunAndSave(true); + } + }); + sketchMenu.add(item); // item = new JMenuItem("Stop"); // item.addActionListener(new ActionListener() { @@ -1508,11 +1511,17 @@ public class Editor extends JFrame implements RunnerListener { // 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 = runAndSaveHandler; + this.presentAndSaveHandler = presentAndSaveHandler; this.stopHandler = stopHandler; this.exportHandler = exportHandler; this.exportAppHandler = exportAppHandler; @@ -1522,6 +1531,8 @@ public class Editor extends JFrame implements RunnerListener { public void resetHandlers() { runHandler = new BuildHandler(); presentHandler = new BuildHandler(true); + runAndSaveHandler = new BuildAndSaveHandler(); + presentAndSaveHandler = new BuildAndSaveHandler(true); stopHandler = new DefaultStopHandler(); exportHandler = new DefaultExportHandler(); exportAppHandler = new DefaultExportAppHandler(); @@ -2012,6 +2023,29 @@ public class Editor extends JFrame implements RunnerListener { // placed on the event thread and causes a hang--bad idea all around. new Thread(verbose ? presentHandler : runHandler).start(); } + + /** + * Implements Sketch → Run and Save. + * @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(); + } class BuildHandler implements Runnable { @@ -2029,7 +2063,7 @@ public class Editor extends JFrame implements RunnerListener { public void run() { try { sketch.prepare(); - sketch.build(verbose); + sketch.build(verbose, false); statusNotice(_("Done compiling.")); } catch (PreferencesMapException e) { statusError(I18n.format( @@ -2044,6 +2078,38 @@ public class Editor extends JFrame implements RunnerListener { toolbar.deactivate(EditorToolbar.RUN); } } + + class BuildAndSaveHandler implements Runnable { + + private final boolean verbose; + + public BuildAndSaveHandler() { + this(false); + } + + public BuildAndSaveHandler(boolean verbose) { + this.verbose = verbose; + } + + @Override + public void run() { + try { + sketch.prepare(); + sketch.build(verbose, true); + statusNotice(_("Done compiling.")); + } catch (PreferencesMapException e) { + statusError(I18n.format( + _("Error while compiling: missing '{0}' configuration parameter"), + e.getMessage())); + } 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 111c1caf8..d36f72522 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -1133,8 +1133,8 @@ public class Sketch { * @return null if compilation failed, main class name if not * @throws RunnerException */ - public String build(boolean verbose) throws RunnerException, PreferencesMapException { - return build(tempBuildFolder.getAbsolutePath(), verbose); + public String build(boolean verbose, boolean save) throws RunnerException, PreferencesMapException { + return build(tempBuildFolder.getAbsolutePath(), verbose, save); } /** @@ -1146,7 +1146,7 @@ public class Sketch { * * @return null if compilation failed, main class name if not */ - public String build(String buildPath, boolean verbose) throws RunnerException, PreferencesMapException { + public String build(String buildPath, boolean verbose, boolean save) throws RunnerException, PreferencesMapException { // run the preprocessor editor.status.progressUpdate(20); @@ -1159,7 +1159,7 @@ public class Sketch { } }; - return Compiler.build(data, buildPath, tempBuildFolder, pl, verbose); + return Compiler.build(data, buildPath, tempBuildFolder, pl, verbose, save); } protected boolean exportApplet(boolean usingProgrammer) throws Exception { @@ -1177,7 +1177,7 @@ public class Sketch { // 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/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index d75822844..dde4ba9f4 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -496,7 +496,7 @@ public class BaseNoGui { // - calls Sketch.build(verbose=false) that calls Sketch.ensureExistence(), set progressListener and calls Compiler.build() // - calls Sketch.upload() (see later...) if (!data.getFolder().exists()) showError(_("No sketch"), _("Can't find the sketch in the specified path"), null); - String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild()); + String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild(), false); if (suggestedClassName == null) showError(_("Error while verifying"), _("An error occurred while verifying the sketch"), null); showMessage(_("Done compiling"), _("Done compiling")); @@ -541,7 +541,7 @@ public class BaseNoGui { // if (!data.getFolder().exists()) showError(...); // String ... = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, verbose); if (!data.getFolder().exists()) showError(_("No sketch"), _("Can't find the sketch in the specified path"), null); - String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild()); + String suggestedClassName = Compiler.build(data, tempBuildFolder.getAbsolutePath(), tempBuildFolder, null, parser.isDoVerboseBuild(), false); if (suggestedClassName == null) showError(_("Error while verifying"), _("An error occurred while verifying the sketch"), null); showMessage(_("Done compiling"), _("Done compiling")); } catch (Exception e) { diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index 1bc0f9074..0e2b886d2 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -67,6 +67,7 @@ public class Compiler implements MessageConsumer { private SketchData sketch; private PreferencesMap prefs; private boolean verbose; + private boolean saveHex; private List objectFiles; @@ -83,7 +84,7 @@ public class Compiler implements MessageConsumer { private ProgressListener progressListener; - static public String build(SketchData data, String buildPath, File tempBuildFolder, ProgressListener progListener, boolean verbose) throws RunnerException, PreferencesMapException { + static public String build(SketchData data, String buildPath, File tempBuildFolder, ProgressListener progListener, boolean verbose, boolean save) throws RunnerException, PreferencesMapException { if (SketchData.checkSketchFile(data.getPrimaryFile()) == null) BaseNoGui.showError(_("Bad file selected"), _("Bad sketch primary file or bad sketch directory structure"), null); @@ -113,7 +114,7 @@ public class Compiler implements MessageConsumer { // compile the program. errors will happen as a RunnerException // that will bubble up to whomever called build(). try { - if (compiler.compile(verbose)) { + if (compiler.compile(verbose, save)) { compiler.size(compiler.getBuildPreferences()); return primaryClassName; } @@ -350,10 +351,11 @@ public class Compiler implements MessageConsumer { * @return true if successful. * @throws RunnerException Only if there's a problem. Only then. */ - public boolean compile(boolean _verbose) throws RunnerException, PreferencesMapException { + public boolean compile(boolean _verbose, boolean _save) throws RunnerException, PreferencesMapException { preprocess(prefs.get("build.path")); verbose = _verbose || PreferencesData.getBoolean("build.verbose"); + saveHex = _save; sketchIsCompiled = false; // Hook runs at Start of Compilation @@ -399,26 +401,26 @@ public class Compiler implements MessageConsumer { } // 1. compile the sketch (already in the buildPath) - progressListener.progress(30); + progressListener.progress(20); compileSketch(includeFolders); sketchIsCompiled = true; // 2. compile the libraries, outputting .o files to: // // Doesn't really use configPreferences - progressListener.progress(40); + progressListener.progress(30); compileLibraries(includeFolders); // 3. compile the core, outputting .o files to and then // collecting them into the core.a library file. - progressListener.progress(50); + progressListener.progress(40); compileCore(); // 4. link it all together into the .elf file - progressListener.progress(60); + progressListener.progress(50); compileLink(); // 5. run objcopy to generate output files - progressListener.progress(75); + progressListener.progress(60); List objcopyPatterns = new ArrayList(); for (String key : prefs.keySet()) { if (key.startsWith("recipe.objcopy.") && key.endsWith(".pattern")) @@ -429,6 +431,12 @@ public class Compiler implements MessageConsumer { runRecipe(recipe); } + // 7. save the hex file + if (saveHex) { + progressListener.progress(80); + saveHex(); + } + progressListener.progress(90); // Hook runs at End of Compilation @@ -1144,6 +1152,37 @@ public class Compiler implements MessageConsumer { } 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 tmp_file = prefs.getOrExcept("recipe.hex.tmp_file"); + tmp_file = StringReplacer.replaceFromMapping(tmp_file, dict); + String save_file = prefs.getOrExcept("recipe.hex.save_file"); + save_file = StringReplacer.replaceFromMapping(save_file, dict); + + File hexFile = new File(prefs.get("build.path") + "/" + tmp_file); + File saveFile = new File(sketch.getFolder().getAbsolutePath() + "/" + save_file); + + FileReader in = new FileReader(hexFile); + FileWriter out = new FileWriter(saveFile); + + int c; + while ((c = in.read()) != -1) + out.write(c); + + in.close(); + out.close(); + + } catch (Exception e) { + throw new RunnerException(e); + } + } + private static String prepareIncludes(List includeFolders) { String res = ""; diff --git a/hardware/arduino/avr/platform.txt b/hardware/arduino/avr/platform.txt index e54919227..f4abb2ae2 100644 --- a/hardware/arduino/avr/platform.txt +++ b/hardware/arduino/avr/platform.txt @@ -71,6 +71,10 @@ recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.f recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.objcopy.eep.flags} {compiler.objcopy.eep.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.eep" 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.tmp_file={build.project_name}.hex +recipe.hex.save_file={build.project_name}.{build.variant}.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 bb2f759e2..9b749ca23 100644 --- a/hardware/arduino/sam/platform.txt +++ b/hardware/arduino/sam/platform.txt @@ -75,6 +75,10 @@ recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.f ## Create output (.bin file) recipe.objcopy.bin.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.tmp_file={build.project_name}.bin +recipe.hex.save_file={build.project_name}.{build.variant}.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]+).*