diff --git a/app/build.xml b/app/build.xml
index f6cf1c33e..7ad00d3cb 100644
--- a/app/build.xml
+++ b/app/build.xml
@@ -101,6 +101,7 @@
+
diff --git a/app/test/cc/arduino/packages/uploaders/MergeSketchWithUploaderTest.java b/app/test/cc/arduino/packages/uploaders/MergeSketchWithUploaderTest.java
new file mode 100644
index 000000000..384aa169d
--- /dev/null
+++ b/app/test/cc/arduino/packages/uploaders/MergeSketchWithUploaderTest.java
@@ -0,0 +1,67 @@
+/*
+ * This file is part of Arduino.
+ *
+ * Arduino is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction. Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License. This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
+ */
+
+package cc.arduino.packages.uploaders;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import processing.app.helpers.FileUtils;
+
+import java.io.File;
+
+import static org.junit.Assert.assertEquals;
+
+public class MergeSketchWithUploaderTest {
+
+ private File sketch;
+
+ @Before
+ public void setup() throws Exception {
+ File originalSketch = new File(MergeSketchWithUploaderTest.class.getResource("/sketch.hex").getFile());
+ sketch = new File(System.getProperty("java.io.tmpdir"), "sketch.hex");
+ FileUtils.copyFile(originalSketch, sketch);
+ }
+
+ @After
+ public void removeTmpFile() {
+ sketch.delete();
+ }
+
+ @Test
+ public void shouldMergeWithOptiboot() throws Exception {
+ assertEquals(11720, sketch.length());
+
+ File bootloader = new File(MergeSketchWithUploaderTest.class.getResource("/optiboot_atmega328.hex").getFile());
+ new MergeSketchWithBooloader().merge(sketch, bootloader);
+ assertEquals(13140, sketch.length());
+ }
+
+
+}
diff --git a/app/test/optiboot_atmega328.hex b/app/test/optiboot_atmega328.hex
new file mode 100644
index 000000000..2a09a245d
--- /dev/null
+++ b/app/test/optiboot_atmega328.hex
@@ -0,0 +1,35 @@
+:107E0000112484B714BE81FFF0D085E080938100F7
+:107E100082E08093C00088E18093C10086E0809377
+:107E2000C20080E18093C4008EE0C9D0259A86E02C
+:107E300020E33CEF91E0309385002093840096BBD3
+:107E4000B09BFECF1D9AA8958150A9F7CC24DD24C4
+:107E500088248394B5E0AB2EA1E19A2EF3E0BF2EE7
+:107E6000A2D0813461F49FD0082FAFD0023811F036
+:107E7000013811F484E001C083E08DD089C08234E0
+:107E800011F484E103C0853419F485E0A6D080C0E4
+:107E9000853579F488D0E82EFF2485D0082F10E0AE
+:107EA000102F00270E291F29000F111F8ED06801E7
+:107EB0006FC0863521F484E090D080E0DECF843638
+:107EC00009F040C070D06FD0082F6DD080E0C81688
+:107ED00080E7D80618F4F601B7BEE895C0E0D1E017
+:107EE00062D089930C17E1F7F0E0CF16F0E7DF06D8
+:107EF00018F0F601B7BEE89568D007B600FCFDCFD4
+:107F0000A601A0E0B1E02C9130E011968C91119780
+:107F100090E0982F8827822B932B1296FA010C0160
+:107F200087BEE89511244E5F5F4FF1E0A038BF0790
+:107F300051F7F601A7BEE89507B600FCFDCF97BE46
+:107F4000E89526C08437B1F42ED02DD0F82E2BD052
+:107F50003CD0F601EF2C8F010F5F1F4F84911BD097
+:107F6000EA94F801C1F70894C11CD11CFA94CF0C13
+:107F7000D11C0EC0853739F428D08EE10CD085E9AC
+:107F80000AD08FE07ACF813511F488E018D01DD067
+:107F900080E101D065CF982F8091C00085FFFCCF94
+:107FA0009093C60008958091C00087FFFCCF809118
+:107FB000C00084FD01C0A8958091C6000895E0E648
+:107FC000F0E098E1908380830895EDDF803219F02E
+:107FD00088E0F5DFFFCF84E1DECF1F93182FE3DFCA
+:107FE0001150E9F7F2DF1F91089580E0E8DFEE27F6
+:047FF000FF270994CA
+:027FFE00040479
+:0400000300007E007B
+:00000001FF
diff --git a/app/test/processing/app/debug/UploaderFactoryTest.java b/app/test/processing/app/debug/UploaderFactoryTest.java
index 827025265..f20df78d4 100644
--- a/app/test/processing/app/debug/UploaderFactoryTest.java
+++ b/app/test/processing/app/debug/UploaderFactoryTest.java
@@ -47,7 +47,6 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest {
@Test
public void shouldCreateAnInstanceOfSSHUploader() throws Exception {
TargetBoard board = new LegacyTargetBoard("yun", new PreferencesMap(new HashMap()), new TargetPlatformStub("id", new TargetPackageStub("id")));
- board.getPreferences().put("upload.via_ssh", "true");
BoardPort boardPort = new BoardPort();
boardPort.setBoardName("yun");
@@ -58,24 +57,9 @@ public class UploaderFactoryTest extends AbstractWithPreferencesTest {
assertTrue(uploader instanceof SSHUploader);
}
- @Test
- public void shouldCreateAnInstanceOfBasicUploaderWhenSSHIsUnsupported() throws Exception {
- TargetBoard board = new LegacyTargetBoard("uno", new PreferencesMap(new HashMap()), new TargetPlatformStub("id", new TargetPackageStub("id")));
- board.getPreferences().put("upload.via_ssh", "false");
-
- BoardPort boardPort = new BoardPort();
- boardPort.setBoardName("myyun");
- boardPort.setAddress("192.168.0.1");
- boardPort.setProtocol("network");
- Uploader uploader = new UploaderFactory().newUploader(board, boardPort, false);
-
- assertTrue(uploader instanceof SerialUploader);
- }
-
@Test
public void shouldCreateAnInstanceOfBasicUploaderWhenPortIsSerial() throws Exception {
TargetBoard board = new LegacyTargetBoard("uno", new PreferencesMap(new HashMap()), new TargetPlatformStub("id", new TargetPackageStub("id")));
- board.getPreferences().put("upload.via_ssh", "false");
BoardPort boardPort = new BoardPort();
boardPort.setBoardName("Arduino Leonardo");
diff --git a/app/test/processing/app/helpers/StringUtilsTest.java b/app/test/processing/app/helpers/StringUtilsTest.java
new file mode 100644
index 000000000..1ddbf51b3
--- /dev/null
+++ b/app/test/processing/app/helpers/StringUtilsTest.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of Arduino.
+ *
+ * Arduino is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction. Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License. This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
+ */
+
+package processing.app.helpers;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class StringUtilsTest {
+
+ @Test
+ public void shouldJoinAnArray() {
+ assertEquals("1 - 2 - 3", StringUtils.join(new String[]{"1", "2", "3"}, " - "));
+ }
+}
diff --git a/app/test/sketch.hex b/app/test/sketch.hex
new file mode 100644
index 000000000..e6c812c07
--- /dev/null
+++ b/app/test/sketch.hex
@@ -0,0 +1,268 @@
+:100000000C941D010C9445010C9445010C94450180
+:100010000C9445010C9445010C9445010C94450148
+:100020000C9445010C9445010C9479050C944504FD
+:100030000C9445010C9445010C9445010C94450128
+:100040000C9445010C9445010C9445010C94450118
+:100050000C9445010C9445010C9445010C945D01F0
+:100060000C9445010C9445010C9445010C944501F8
+:100070000C9445010C9445010C9445010C944501E8
+:100080000C9445010C9445010C9445010C944501D8
+:100090000C9445010C9445010C9445010C944501C8
+:1000A0000C9445010C9445010C94450100C18081DC
+:1000B000C11201000202000040412336800001010C
+:1000C00002000112010002000000404123368000BE
+:1000D000010102000141726475696E6F204C4C434E
+:1000E0000041726475696E6F204C656F6E61726459
+:1000F0006F000403090400000000000000002A2B28
+:1001000028000000000000000000000000000000C7
+:100110000000000000002C9EB4A0A1A2A434A6A759
+:10012000A5AE362D3738271E1F2021222324252651
+:10013000B333B62EB7B89F8485868788898A8B8C1F
+:100140008D8E8F909192939495969798999A9B9C67
+:100150009D2F3130A3AD350405060708090A0B0CA5
+:100160000D0E0F101112131415161718191A1B1C47
+:100170001DAFB1B0B5000904020001030000000981
+:1001800021010100012265000705840340000105EB
+:10019000010902A1010901A100850105091901292F
+:1001A00003150025019503750181029501750581EF
+:1001B0000305010930093109381581257F75089536
+:1001C000038106C0C005010906A1018502050719C2
+:1001D000E029E71500250175019508810295017553
+:1001E000088103950675081500256505071900297E
+:1001F000658100C0080B0002020201000904000032
+:10020000010202000005240010010524010101047F
+:100210002402060524060001070581031000400999
+:10022000040100020A000000070502024000000766
+:100230000583024000002307150811241FBECFEFDD
+:10024000DAE0DEBFCDBF11E0A0E0B1E0E4E7F0E12D
+:1002500002C005900D92A832B107D9F721E0A8E2BB
+:10026000B1E001C01D92A739B207E1F712E0CAE37D
+:10027000D2E004C02297FE010E943408C633D107A1
+:10028000C9F70E944A010C9438080C9400000895A4
+:10029000089508950E94F2010E94490181E391E0CE
+:1002A0000E94CF050E944701C0E0D0E00E944801B3
+:1002B0002097E1F30E940000F9CF1F920F920FB632
+:1002C0000F9211242F933F938F939F93AF93BF93DC
+:1002D0008091290190912A01A0912B01B0912C01CC
+:1002E0003091280123E0230F2D3720F40196A11D22
+:1002F000B11D05C026E8230F0296A11DB11D209354
+:1003000028018093290190932A01A0932B01B09397
+:100310002C0180912D0190912E01A0912F01B0917F
+:1003200030010196A11DB11D80932D0190932E01E6
+:10033000A0932F01B0933001BF91AF919F918F9106
+:100340003F912F910F900FBE0F901F9018953FB7C0
+:10035000F89480912D0190912E01A0912F01B091E0
+:10036000300126B5A89B05C02F3F19F00196A11DAD
+:10037000B11D3FBF6627782F892F9A2F620F711DFD
+:10038000811D911D42E0660F771F881F991F4A95B6
+:10039000D1F70895CF92DF92EF92FF92CF93DF9340
+:1003A0006B017C010E94A701EB01C114D104E1049F
+:1003B000F10489F00E9448020E94A7016C1B7D0B8A
+:1003C000683E734090F381E0C81AD108E108F10853
+:1003D000C851DC4FEACFDF91CF91FF90EF90DF90D3
+:1003E000CF900895789484B5826084BD84B581608F
+:1003F00084BD85B5826085BD85B5816085BDEEE62D
+:10040000F0E0808181608083E1E8F0E0108280810B
+:1004100082608083808181608083E0E8F0E0808179
+:1004200081608083E1E9F0E0808182608083808167
+:1004300081608083E0E9F0E0808181608083E1EC8D
+:10044000F0E080818460808380818260808380810D
+:1004500081608083E3ECF0E0808181608083E0EC68
+:10046000F0E0808182608083E2ECF0E08081816056
+:100470008083EAE7F0E0808184608083808182600D
+:1004800080838081816080838081806880830895FB
+:1004900008954091350150913601209133013091FA
+:1004A000340142175307B4F49091E8009570E1F3DA
+:1004B0009091E80092FD19C08093F1008091350180
+:1004C0009091360101968F739927892B19F48EEF3D
+:1004D0008093E800809135019091360101969093C8
+:1004E00036018093350181E0089580E00895CF9230
+:1004F000DF92FF920F931F93CF93DF931F92CDB79D
+:10050000DEB7082F162F862F880F8E5F99830E94E3
+:10051000490283E00E944902F02EC02E9981D92E13
+:100520008C2D8F19811778F4F60184910E9449026D
+:10053000082F80E00E9449028023FFEFCF1ADF0AD4
+:100540008111EECF01C081E00F90DF91CF911F911B
+:100550000F91FF90DF90CF900895615030F020917F
+:10056000F100FC0120830196F8CF289884E680935F
+:10057000380108952FB7FC012083F89467706093C9
+:10058000E9000895CF93DF931F92CDB7DEB7682FB0
+:10059000CE0101960E94BA028091F20099819FBF1C
+:1005A0000F90DF91CF910895FF920F931F93CF93F8
+:1005B000DF9300D0CDB7DEB7F62E8A0190913701D8
+:1005C000992311F057FF03C08FEF9FEF2BC0682FC7
+:1005D000CE0101967A830E94BA028091F20090E0E7
+:1005E000A8017A81801791070CF4AC01EF2DF72F49
+:1005F000F40E84E6FE1639F0289880933801909125
+:10060000F1009193F7CF4115510521F08091F2004F
+:10061000882321F089818FBFCA0104C08BE68093B3
+:10062000E800F8CF0F900F90DF91CF911F910F91BD
+:10063000FF900895CF93DF931F92CDB7DEB741E0CF
+:1006400050E0BE016F5F7F4F0E94D402019719F402
+:10065000898190E002C08FEF9FEF0F90DF91CF91E3
+:100660000895CF93DF931F92CDB7DEB7682FCE01E9
+:1006700001960E94BA029091E800892F807295FF3E
+:1006800004C09091F20080E4891B99819FBF0F9074
+:10069000DF91CF9108956F927F928F929F92AF9248
+:1006A000BF92CF92DF92EF92FF920F931F93CF935F
+:1006B000DF931F92CDB7DEB7782E7B01C42EB52E07
+:1006C00080913701882369F0042F152F8AEFD82EE7
+:1006D000872D8072982E9AE3A92E872D8074882EFC
+:1006E00011C08FEF9FEF57C0872D0E943103682EF6
+:1006F00081110CC0DA94A9F361E070E080E090E031
+:100700000E94CA010115110579F73BC0282F30E07E
+:10071000021713070CF4602E672DCE0101960E947C
+:10072000BA028091E80085FF29C0262D30E0021B27
+:10073000130B992039F06A948FEF6816B1F010927C
+:10074000F100F9CFF701862D77FE07C0815058F0F0
+:1007500094919093F1003196F9CF815020F09191CE
+:100760009093F100FACFE20EF31E8091E80085FF2E
+:100770000FC00115110511F481100AC089818FBFC6
+:10078000C1CF5D9884E6809339018C2D9B2D03C0E9
+:10079000A092E800F3CF0F90DF91CF911F910F91BE
+:1007A000FF90EF90DF90CF90BF90AF909F908F9091
+:1007B0007F906F9008951092E90010923601109288
+:1007C000350190933401809333010895CF92DF92E5
+:1007D000FF920F931F93CF93DF9300D0CDB7DEB777
+:1007E000F82E8A016B0101151105B1F0F601F7FE33
+:1007F00002C0849101C0808149835A830E944902CA
+:1008000001501109FFEFCF1ADF0A49815A81811186
+:10081000EACF8FEF9FEF01C0CA010F900F90DF91D9
+:10082000CF911F910F91FF90DF90CF9008951F936C
+:10083000CF93DF931F92CDB7DEB7162F2091E8003C
+:1008400022FFFCCF612F79830E94AD028BEF809352
+:10085000E800812F7981972F0F90DF91CF911F9121
+:100860000895CF93DF931F92CDB7DEB71982CE01E3
+:1008700001960E94B507CE0101960E940B06898160
+:1008800090E00F90DF91CF9108951F920F920FB6D5
+:100890000F921124EF92FF920F931F932F933F9388
+:1008A0004F935F936F937F938F939F93AF93BF9378
+:1008B000EF93FF93CF93DF93CDB7DEB76297DEBFA1
+:1008C000CDBF1092E9008091E80083FFEBC068E0A3
+:1008D000CE010A960E94AD0282EF8093E8009A85CD
+:1008E00097FF05C08091E80080FFFCCF03C08EEF2A
+:1008F0008093E800892F807609F0B9C08B8581113B
+:1009000005C01092F1001092F100C5C0282F2D7F74
+:10091000213009F4C0C0853049F48091E80080FF9F
+:10092000FCCF8C8580688093E300B5C0863009F0E9
+:1009300076C02D85E888F988223071F580E090E056
+:100940002A8B0E94DB030E94310499E08E010F5F25
+:100950001F4FF801392F11923A95E9F799832A89A7
+:100960002A8391E09E8390E898879AEF9987209157
+:10097000350130913601275F3F4F3C832B838D83B8
+:10098000C7010E94DB0349E050E0B80180E00E940B
+:10099000E6030E9431047FC0C7012A8B0E94DB035B
+:1009A0002A89223241F482E290E00E941606892BC5
+:1009B00009F071C074C0213069F488899989089759
+:1009C00011F42093320180913201811118C063EC3F
+:1009D00070E01AC0233009F062C08C85882391F042
+:1009E000823021F460E181EE90E006C0813009F0B0
+:1009F00056C06BE085ED90E00E9477024AC061EB43
+:100A000070E002C062EF70E06115710509F447C043
+:100A1000FB01449150E080E80E94E6033CC087302F
+:100A200009F43DC0883021F481E08093F10033C0A7
+:100A3000893089F5937099F5EDEAF0E081E021E0E5
+:100A400096E38093E9002093EB0034913093EC001F
+:100A50009093ED008F5F3196853099F78EE7809304
+:100A6000EA001092EA008C858093370114C08889CF
+:100A700099890E94DB038E85811105C0CE010A96FB
+:100A80000E94C00706C0823051F4CE010A960E942F
+:100A90003E06882321F08EEF8093E80003C081E2B8
+:100AA0008093EB0062960FB6F894DEBF0FBECDBF09
+:100AB000DF91CF91FF91EF91BF91AF919F918F9176
+:100AC0007F916F915F914F913F912F911F910F9166
+:100AD000FF90EF900F900FBE0F901F90189580938E
+:100AE000E9008091F200882319F08AE38093E800FE
+:100AF00008951F920F920FB60F9211242F933F93D8
+:100B00004F935F936F937F938F939F93AF93BF9315
+:100B1000EF93FF938091E1001092E10083FF0FC0FB
+:100B20001092E90091E09093EB001092EC0092E3B8
+:100B30009093ED001092370198E09093F00082FFBF
+:100B40001DC083E00E946F0580913901882339F030
+:100B500080913901815080933901882369F0809117
+:100B60003801882359F080913801815080933801F1
+:100B7000811104C0289A02C05D9AF1CFFF91EF91D4
+:100B8000BF91AF919F918F917F916F915F914F91A5
+:100B90003F912F910F900FBE0F901F9018951092BC
+:100BA000370181E08093D70080EA8093D80082E10A
+:100BB00089BD09B400FEFDCF61E070E080E090E007
+:100BC0000E94CA0180E98093D8008CE08093E20003
+:100BD0001092E000559A209A0895FF920F931F9368
+:100BE000CF93DF93EC01F62EE881F9810480F58143
+:100BF000E02D09958C01E881F9810680F781E02DCF
+:100C00006F2DCE010995C8019927DF91CF911F91D2
+:100C10000F91FF900895FC0120812F5F208349E10F
+:100C200050E066E771E080E80C94E60345E650E0AA
+:100C30006FE871E080E80C94E603EF92FF920F9367
+:100C40001F93CF93DF931F92CDB7DEB789838B01BC
+:100C50007A0141E050E0BE016F5F7F4F84E00E9467
+:100C60004B03A701B80184E40E944B030F90DF916E
+:100C7000CF911F910F91FF90EF900895FC0191810A
+:100C80008081813A31F481E0913091F0933089F4A0
+:100C900011C0813271F49B3021F482818093010173
+:100CA00005C09A3031F482818093000181E008957B
+:100CB000089580E0089548E050E082E00C941D061D
+:100CC000CF93DF93DC01683818F0E8E7E60F25C022
+:100CD000E62FF0E067FF11C0E058F10981E090E0F5
+:100CE00001C0880FEA95EAF714969C911497982B07
+:100CF00014969C931497E0E010C0EA50FF4FE491E3
+:100D0000EE2309F440C0E7FF08C014968C911497B5
+:100D1000826014968C931497EF7716968C911697A1
+:100D20008E1741F117968C9117978E1719F1189617
+:100D30008C9118978E17F1F019968C9119978E1740
+:100D4000C9F01A968C911A978E17A1F01B968C9168
+:100D50001B978E1779F080E090E0ED01C80FD91F46
+:100D60002E81211102C0EE8305C0019686309105C7
+:100D7000A1F709C0BD016C5F7F4FCD010E945B06EA
+:100D800081E090E008C081E090E013969C938E9300
+:100D9000129780E090E0DF91CF910895683818F0C5
+:100DA000E8E7E60F25C0E62FF0E067FF12C0E05845
+:100DB000F10921E030E001C0220FEA95EAF7209521
+:100DC000DC0114963C911497322314963C93E0E096
+:100DD0000FC0EA50FF4FE491EE2329F1E7FF08C06E
+:100DE000DC0114962C9114972D7F14962C93EF7799
+:100DF00020E030E0EE2351F0DC01A20FB31F169685
+:100E00004C9116974E1302C016961C922F5F3F4FBF
+:100E10002630310579F7BC016C5F7F4F0E945B067D
+:100E200081E090E0089580E090E00895FC01168252
+:100E3000178210861186128613861482BC016C5F9D
+:100E40007F4F0C945B061092460110923D01109268
+:100E50003C018EE091E090933B0180933A0108952C
+:100E6000CF92DF92EF92FF920F931F93CF93DF9376
+:100E70006C017A01EB01E60EF71E00E010E0CE15E2
+:100E8000DF0561F06991D601ED91FC910190F0814F
+:100E9000E02DC6010995080F191FF1CFC801DF9198
+:100EA000CF911F910F91FF90EF90DF90CF90089519
+:100EB000CF93DF931F92CDB7DEB76983DC01ED914D
+:100EC000FC910280F381E02D41E050E0BE016F5FB4
+:100ED0007F4F09950F90DF91CF910895CF93DF93C6
+:100EE000EC018C859D8597FF05C082E00E941A0366
+:100EF0009D878C878C859D85DF91CF91089583E0B8
+:100F00000C946F05FC018485958597FD06C082E0F1
+:100F10000E94C20290E00196089582E00E94C202FF
+:100F200090E00895FC018485958597FD05C02FEF1D
+:100F30003FEF35872487089582E00C941A03CF93FE
+:100F4000DF93EC0180910901882331F083E00E9456
+:100F50004B031816190634F081E090E09B838A83D6
+:100F600080E090E0DF91CF910895FC0120812E5F19
+:100F7000208342E450E064EF71E080E80C94E603E3
+:100F8000FC0181819081913A59F4813209F03CC091
+:100F900047E050E062E071E080E00E94E60343C079
+:100FA000913291F5803239F467E070E082E091E0AF
+:100FB0000E94170406C0823209F035C082818093F6
+:100FC00009018091020190910301A0910401B09167
+:100FD0000501803B9440A105B105C1F48091090150
+:100FE00080FD14C087E797E790930108809300087D
+:100FF0002BE088E190E00FB6F894A895809360000C
+:101000000FBE209360000FC080E0089588E10FB606
+:10101000F89480936000109260000FBEA895109223
+:1010200001081092000881E0089510924A01109280
+:10103000490188EE93E0A0E0B0E080934B019093EB
+:101040004C01A0934D01B0934E018CE191E090933F
+:101050004801809347018FEF9FEF90935401809355
+:1010600053010895EE0FFF1F0590F491E02D0994B0
+:04107000F894FFCF22
+:10107400010100E100000000000000000000ED0597
+:1010840030076006CE0616070000000058079F07C9
+:08109400820792076E077F0737
+:00000001FF
diff --git a/arduino-core/src/cc/arduino/packages/UploaderFactory.java b/arduino-core/src/cc/arduino/packages/UploaderFactory.java
index 56828a753..860d3da8f 100644
--- a/arduino-core/src/cc/arduino/packages/UploaderFactory.java
+++ b/arduino-core/src/cc/arduino/packages/UploaderFactory.java
@@ -40,7 +40,7 @@ public class UploaderFactory {
return new SerialUploader(true);
}
- if ("true".equals(board.getPreferences().get("upload.via_ssh")) && port != null && "network".equals(port.getProtocol())) {
+ if (port != null && "network".equals(port.getProtocol())) {
return new SSHUploader(port);
}
diff --git a/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java b/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java
index 48d9cdd4b..7413062a8 100644
--- a/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java
+++ b/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java
@@ -141,9 +141,11 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
PreferencesMap prefs = null;
String board = null;
+ String description = null;
if (info.hasData()) {
prefs = new PreferencesMap();
board = info.getPropertyString("board");
+ description = info.getPropertyString("description");
prefs.put("board", board);
prefs.put("distro_version", info.getPropertyString("distro_version"));
prefs.put("port", "" + info.getPort());
@@ -155,6 +157,8 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
if (boardName != null) {
label += " (" + boardName + ")";
}
+ } else if (description != null) {
+ label += " (" + description + ")";
}
BoardPort port = new BoardPort();
diff --git a/arduino-core/src/cc/arduino/packages/uploaders/MergeSketchWithBooloader.java b/arduino-core/src/cc/arduino/packages/uploaders/MergeSketchWithBooloader.java
new file mode 100644
index 000000000..a6f34b265
--- /dev/null
+++ b/arduino-core/src/cc/arduino/packages/uploaders/MergeSketchWithBooloader.java
@@ -0,0 +1,60 @@
+/*
+ * This file is part of Arduino.
+ *
+ * Arduino is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * As a special exception, you may use this file as part of a free software
+ * library without restriction. Specifically, if other files instantiate
+ * templates or use macros or inline functions from this file, or you compile
+ * this file and link it with other files to produce an executable, this
+ * file does not by itself cause the resulting executable to be covered by
+ * the GNU General Public License. This exception does not however
+ * invalidate any other reasons why the executable file might be covered by
+ * the GNU General Public License.
+ *
+ * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
+ */
+
+package cc.arduino.packages.uploaders;
+
+import processing.app.helpers.FileUtils;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+public class MergeSketchWithBooloader {
+
+ public void merge(File sketch, File bootloader) throws IOException {
+ List mergedSketch = FileUtils.readFileToListOfStrings(sketch);
+ mergedSketch.remove(mergedSketch.size() - 1);
+ mergedSketch.addAll(FileUtils.readFileToListOfStrings(bootloader));
+
+ FileWriter writer = null;
+ try {
+ writer = new FileWriter(sketch);
+ for (String line : mergedSketch) {
+ writer.write(line);
+ writer.write("\n");
+ }
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+
+}
diff --git a/arduino-core/src/cc/arduino/packages/uploaders/SSHUploader.java b/arduino-core/src/cc/arduino/packages/uploaders/SSHUploader.java
index cadb05736..99f5a51c7 100644
--- a/arduino-core/src/cc/arduino/packages/uploaders/SSHUploader.java
+++ b/arduino-core/src/cc/arduino/packages/uploaders/SSHUploader.java
@@ -38,10 +38,8 @@ import com.jcraft.jsch.Session;
import processing.app.BaseNoGui;
import processing.app.I18n;
import processing.app.PreferencesData;
-import processing.app.debug.RunnerException;
-import processing.app.debug.TargetPlatform;
-import processing.app.helpers.PreferencesMap;
-import processing.app.helpers.StringUtils;
+import processing.app.debug.*;
+import processing.app.helpers.*;
import java.io.File;
import java.io.IOException;
@@ -70,11 +68,33 @@ public class SSHUploader extends Uploader {
}
@Override
- public boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer, List warningsAccumulator) throws RunnerException {
+ public boolean uploadUsingPreferences(File sourcePath, String buildPath, String className, boolean usingProgrammer, List warningsAccumulator) throws RunnerException, PreferencesMapException {
if (usingProgrammer) {
throw new RunnerException(_("Network upload using programmer not supported"));
}
+ TargetPlatform targetPlatform = BaseNoGui.getTargetPlatform();
+ PreferencesMap prefs = PreferencesData.getMap();
+ prefs.putAll(BaseNoGui.getBoardPreferences());
+ String tool = prefs.getOrExcept("upload.tool");
+ if (tool.contains(":")) {
+ String[] split = tool.split(":", 2);
+ targetPlatform = BaseNoGui.getCurrentTargetPlatformFromPackage(split[0]);
+ tool = split[1];
+ }
+ prefs.putAll(targetPlatform.getTool(tool));
+
+ boolean coreMissesRemoteUploadTool = targetPlatform.getTool(tool + "_remote").isEmpty();
+
+ if (coreMissesRemoteUploadTool) {
+ prefs.put("upload.pattern", "/usr/bin/run-avrdude /tmp/sketch.hex");
+ } else {
+ prefs.putAll(targetPlatform.getTool(tool + "_remote"));
+ }
+
+ prefs.put("build.path", buildPath);
+ prefs.put("build.project_name", className);
+
Session session = null;
SCP scp = null;
try {
@@ -88,9 +108,21 @@ public class SSHUploader extends Uploader {
scp = new SCP(session);
SSH ssh = new SSH(session);
- scpFiles(scp, ssh, sourcePath, buildPath, className, warningsAccumulator);
+ File mergedSketch = new File(buildPath, className + ".with_bootloader.hex");
- return runAVRDude(ssh);
+ File sketchToCopy;
+ if (!coreMissesRemoteUploadTool && mergedSketch.exists()) {
+ sketchToCopy = mergedSketch;
+ } else {
+ sketchToCopy = processing.app.debug.Compiler.findCompiledSketch(prefs);
+ }
+ scpFiles(scp, ssh, sourcePath, sketchToCopy, warningsAccumulator);
+
+ if (coreMissesRemoteUploadTool) {
+ ssh.execSyncCommand("merge-sketch-with-bootloader.lua /tmp/sketch.hex", System.out, System.err);
+ }
+
+ return runUploadTool(ssh, prefs);
} catch (JSchException e) {
String message = e.getMessage();
if ("Auth cancel".equals(message) || "Auth fail".equals(message)) {
@@ -116,28 +148,32 @@ public class SSHUploader extends Uploader {
}
}
- private boolean runAVRDude(SSH ssh) throws IOException, JSchException {
- TargetPlatform targetPlatform = BaseNoGui.getTargetPlatform();
- PreferencesMap prefs = PreferencesData.getMap();
- PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
- if (boardPreferences != null) {
- prefs.putAll(boardPreferences);
- }
- prefs.putAll(targetPlatform.getTool(prefs.get("upload.tool")));
-
- String additionalParams = verbose ? prefs.get("upload.params.verbose") : prefs.get("upload.params.quiet");
-
- boolean success = ssh.execSyncCommand("merge-sketch-with-bootloader.lua /tmp/sketch.hex", System.out, System.err);
+ private boolean runUploadTool(SSH ssh, PreferencesMap prefs) throws Exception {
ssh.execSyncCommand("kill-bridge");
- success = success && ssh.execSyncCommand("run-avrdude /tmp/sketch.hex '" + additionalParams + "'", System.out, System.err);
- return success;
+
+ if (verbose) {
+ prefs.put("upload.verbose", prefs.getOrExcept("upload.params.verbose"));
+ } else {
+ prefs.put("upload.verbose", prefs.getOrExcept("upload.params.quiet"));
+ }
+
+ String pattern = prefs.getOrExcept("upload.pattern");
+ String[] cmd = StringReplacer.formatAndSplit(pattern, prefs, true);
+ return ssh.execSyncCommand(StringUtils.join(cmd, " "), System.out, System.err);
}
- private void scpFiles(SCP scp, SSH ssh, File sourcePath, String buildPath, String className, List warningsAccumulator) throws JSchException, IOException {
+ private void scpFiles(SCP scp, SSH ssh, File sourcePath, File sketch, List warningsAccumulator) throws JSchException, IOException {
+ String uploadedSketchFileName;
+ if (sketch.getName().endsWith("hex")) {
+ uploadedSketchFileName = "sketch.hex";
+ } else {
+ uploadedSketchFileName = "sketch.bin";
+ }
+
try {
scp.open();
scp.startFolder("tmp");
- scp.sendFile(new File(buildPath, className + ".hex"), "sketch.hex");
+ scp.sendFile(sketch, uploadedSketchFileName);
scp.endFolder();
if (canUploadWWWFiles(sourcePath, ssh, warningsAccumulator)) {
diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java
index 3de715384..42393add1 100644
--- a/arduino-core/src/processing/app/debug/Compiler.java
+++ b/arduino-core/src/processing/app/debug/Compiler.java
@@ -26,22 +26,14 @@ package processing.app.debug;
import static processing.app.I18n._;
import java.io.*;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.Date;
-import java.util.GregorianCalendar;
+import java.util.*;
import cc.arduino.MyStreamPumper;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.Uploader;
import cc.arduino.packages.UploaderFactory;
+import cc.arduino.packages.uploaders.MergeSketchWithBooloader;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.exec.*;
import processing.app.BaseNoGui;
@@ -81,7 +73,7 @@ public class Compiler implements MessageConsumer {
* Listener interface for progress update on the GUI
*/
public interface ProgressListener {
- public void progress(int percent);
+ void progress(int percent);
}
private ProgressListener progressListener;
@@ -180,6 +172,17 @@ public class Compiler implements MessageConsumer {
return success;
}
+ static public File findCompiledSketch(PreferencesMap prefs) throws PreferencesMapException {
+ List paths = Arrays.asList("{build.path}/{build.project_name}.hex", "{build.path}/{build.project_name}.bin");
+ Optional sketch = paths.stream().
+ map(path -> StringReplacer.replaceFromMapping(path, prefs)).
+ map(File::new).
+ filter(File::exists).
+ findFirst();
+ return sketch.orElseThrow(() -> new IllegalStateException(_("No compiled sketch found")));
+ }
+
+
/**
* Create a new Compiler
* @param _sketch Sketch object to be compiled.
@@ -454,6 +457,14 @@ public class Compiler implements MessageConsumer {
runActions("hooks.objcopy.postobjcopy", prefs);
+ progressListener.progress(70);
+ try {
+ mergeSketchWithBootloaderIfAppropriate(sketch.getName() + ".cpp", prefs);
+ } catch (IOException e) {
+ e.printStackTrace();
+ // ignore
+ }
+
// 7. save the hex file
if (saveHex) {
runActions("hooks.savehex.presavehex", prefs);
@@ -545,20 +556,26 @@ public class Compiler implements MessageConsumer {
p.put("compiler.path", BaseNoGui.getAvrBasePath());
}
+ TargetPlatform referencePlatform = null;
+ if (corePlatform != null) {
+ referencePlatform = corePlatform;
+ } else {
+ referencePlatform = targetPlatform;
+ }
+
+ p.put("build.platform.path", referencePlatform.getFolder().getAbsolutePath());
+
// Core folder
- TargetPlatform tp = corePlatform;
- if (tp == null)
- tp = targetPlatform;
- File coreFolder = new File(tp.getFolder(), "cores");
+ File coreFolder = new File(referencePlatform.getFolder(), "cores");
coreFolder = new File(coreFolder, core);
p.put("build.core", core);
p.put("build.core.path", coreFolder.getAbsolutePath());
// System Folder
- File systemFolder = tp.getFolder();
+ File systemFolder = referencePlatform.getFolder();
systemFolder = new File(systemFolder, "system");
p.put("build.system.path", systemFolder.getAbsolutePath());
-
+
// Variant Folder
String variant = p.get("build.variant");
if (variant != null) {
@@ -1169,7 +1186,33 @@ public class Compiler implements MessageConsumer {
}
execAsynchronously(cmdArray);
}
-
+
+ private File mergeSketchWithBootloaderIfAppropriate(String className, PreferencesMap prefs) throws IOException {
+ if (!prefs.containsKey("bootloader.noblink") && !prefs.containsKey("bootloader.file")) {
+ return null;
+ }
+
+ String buildPath = prefs.get("build.path");
+ File sketch = new File(buildPath, className + ".hex");
+ if (!sketch.exists()) {
+ return null;
+ }
+
+ File mergedSketch = new File(buildPath, className + ".with_bootloader.hex");
+ FileUtils.copyFile(sketch, mergedSketch);
+
+ String bootloaderNoBlink = prefs.get("bootloader.noblink");
+ if (bootloaderNoBlink == null) {
+ bootloaderNoBlink = prefs.get("bootloader.file");
+ }
+
+ File bootloader = new File(new File(prefs.get("build.platform.path"), "bootloaders"), bootloaderNoBlink);
+
+ new MergeSketchWithBooloader().merge(mergedSketch, bootloader);
+
+ return mergedSketch;
+ }
+
//7. Save the .hex file
void saveHex() throws RunnerException {
if (!prefs.containsKey("recipe.output.tmp_file") || !prefs.containsKey("recipe.output.save_file")) {
@@ -1181,9 +1224,23 @@ public class Compiler implements MessageConsumer {
dict.put("ide_version", "" + BaseNoGui.REVISION);
try {
- String compiledSketch = prefs.getOrExcept("recipe.output.tmp_file");
+ List compiledSketches = new ArrayList(prefs.subTree("recipe.output.tmp_file", 1).values());
+ if (!compiledSketches.isEmpty()) {
+ List copyOfCompiledSketches = new ArrayList(prefs.subTree("recipe.output.save_file", 1).values());
+ for (int i = 0; i < compiledSketches.size(); i++) {
+ saveHex(compiledSketches.get(i), copyOfCompiledSketches.get(i), prefs);
+ }
+ } else {
+ saveHex(prefs.getOrExcept("recipe.output.tmp_file"), prefs.getOrExcept("recipe.output.save_file"), prefs);
+ }
+ } catch (Exception e) {
+ throw new RunnerException(e);
+ }
+ }
+
+ private void saveHex(String compiledSketch, String copyOfCompiledSketch, PreferencesMap dict) throws RunnerException {
+ try {
compiledSketch = StringReplacer.replaceFromMapping(compiledSketch, dict);
- String copyOfCompiledSketch = prefs.getOrExcept("recipe.output.save_file");
copyOfCompiledSketch = StringReplacer.replaceFromMapping(copyOfCompiledSketch, dict);
File compiledSketchFile = new File(prefs.get("build.path"), compiledSketch);
@@ -1194,7 +1251,6 @@ public class Compiler implements MessageConsumer {
throw new RunnerException(e);
}
}
-
private static String prepareIncludes(List includeFolders) {
String res = "";
diff --git a/arduino-core/src/processing/app/helpers/FileUtils.java b/arduino-core/src/processing/app/helpers/FileUtils.java
index 9d138b841..fdc0b2570 100644
--- a/arduino-core/src/processing/app/helpers/FileUtils.java
+++ b/arduino-core/src/processing/app/helpers/FileUtils.java
@@ -3,10 +3,7 @@ package processing.app.helpers;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
+import java.util.*;
import java.util.regex.Pattern;
public class FileUtils {
@@ -91,8 +88,7 @@ public class FileUtils {
}
public static File createTempFolderIn(File parent) throws IOException {
- File tmpFolder = new File(parent, "arduino_"
- + new Random().nextInt(1000000));
+ File tmpFolder = new File(parent, "arduino_" + new Random().nextInt(1000000));
if (!tmpFolder.mkdir()) {
throw new IOException("Unable to create temp folder " + tmpFolder);
}
@@ -195,27 +191,46 @@ public class FileUtils {
}
}
+ public static List readFileToListOfStrings(File file) throws IOException {
+ List strings = new LinkedList();
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = line.replaceAll("\r", "").replaceAll("\n", "").replaceAll(" ", "");
+ strings.add(line);
+ }
+ return strings;
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+ }
+
+
/**
* Returns true if the given file has any of the given extensions.
- * @param file
- * File whose name to look at
- * @param extensions
- * Extensions to consider (just the extension, without the
- * dot). Should all be lowercase, case insensitive matching
- * is used.
+ *
+ * @param file File whose name to look at
+ * @param extensions Extensions to consider (just the extension, without the
+ * dot). Should all be lowercase, case insensitive matching
+ * is used.
*/
public static boolean hasExtension(File file, String... extensions) {
return hasExtension(file, Arrays.asList(extensions));
}
public static boolean hasExtension(File file, List extensions) {
- String pieces[] = file.getName().split("\\.");
- if (pieces.length < 2)
- return false;
+ String pieces[] = file.getName().split("\\.");
+ if (pieces.length < 2) {
+ return false;
+ }
- String extension = pieces[pieces.length - 1];
+ String extension = pieces[pieces.length - 1];
- return extensions.contains(extension.toLowerCase());
+ return extensions.contains(extension.toLowerCase());
}
@@ -224,15 +239,12 @@ public class FileUtils {
* extension. Excludes hidden files and folders and
* source control folders.
*
- * @param folder
- * Folder to look into
- * @param recursive
- * true will recursively find all files in sub-folders
- * @param extensions
- * A list of file extensions to search (just the extension,
- * without the dot). Should all be lowercase, case
- * insensitive matching is used. If no extensions are
- * passed, all files are returned.
+ * @param folder Folder to look into
+ * @param recursive true will recursively find all files in sub-folders
+ * @param extensions A list of file extensions to search (just the extension,
+ * without the dot). Should all be lowercase, case
+ * insensitive matching is used. If no extensions are
+ * passed, all files are returned.
* @return
*/
public static List listFiles(File folder, boolean recursive,
diff --git a/arduino-core/src/processing/app/helpers/StringReplacer.java b/arduino-core/src/processing/app/helpers/StringReplacer.java
index 19f7bce58..f51bfd7b6 100644
--- a/arduino-core/src/processing/app/helpers/StringReplacer.java
+++ b/arduino-core/src/processing/app/helpers/StringReplacer.java
@@ -92,9 +92,9 @@ public class StringReplacer {
public static String replaceFromMapping(String src, Map map,
String leftDelimiter,
String rightDelimiter) {
- for (String k : map.keySet()) {
- String keyword = leftDelimiter + k + rightDelimiter;
- src = src.replace(keyword, map.get(k));
+ for (Map.Entry entry : map.entrySet()) {
+ String keyword = leftDelimiter + entry.getKey() + rightDelimiter;
+ src = src.replace(keyword, entry.getValue());
}
return src;
}
diff --git a/arduino-core/src/processing/app/helpers/StringUtils.java b/arduino-core/src/processing/app/helpers/StringUtils.java
index fb60df330..d17e7022c 100644
--- a/arduino-core/src/processing/app/helpers/StringUtils.java
+++ b/arduino-core/src/processing/app/helpers/StringUtils.java
@@ -40,4 +40,12 @@ public class StringUtils {
}
return s.substring(0, i + 1);
}
+
+ public static String join(String[] arr, String separator) {
+ StringBuffer sb = new StringBuffer();
+ for (String s : arr) {
+ sb.append(s).append(separator);
+ }
+ return sb.substring(0, sb.length() - separator.length());
+ }
}