PageRenderTime 24ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/arduino-core/src/cc/arduino/Compiler.java

https://github.com/cmaglie/Arduino
Java | 621 lines | 481 code | 94 blank | 46 comment | 79 complexity | f63f2057d0563801e5925472f5a3e9bb MD5 | raw file
  1. /*
  2. * This file is part of Arduino.
  3. *
  4. * Copyright 2015 Arduino LLC (http://www.arduino.cc/)
  5. *
  6. * Arduino is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  19. *
  20. * As a special exception, you may use this file as part of a free software
  21. * library without restriction. Specifically, if other files instantiate
  22. * templates or use macros or inline functions from this file, or you compile
  23. * this file and link it with other files to produce an executable, this
  24. * file does not by itself cause the resulting executable to be covered by
  25. * the GNU General Public License. This exception does not however
  26. * invalidate any other reasons why the executable file might be covered by
  27. * the GNU General Public License.
  28. */
  29. package cc.arduino;
  30. import cc.arduino.i18n.I18NAwareMessageConsumer;
  31. import cc.arduino.packages.BoardPort;
  32. import org.apache.commons.exec.CommandLine;
  33. import org.apache.commons.exec.DefaultExecutor;
  34. import org.apache.commons.exec.PumpStreamHandler;
  35. import org.apache.commons.lang3.StringUtils;
  36. import processing.app.*;
  37. import processing.app.debug.*;
  38. import processing.app.helpers.PreferencesMap;
  39. import processing.app.helpers.PreferencesMapException;
  40. import processing.app.helpers.ProcessUtils;
  41. import processing.app.helpers.StringReplacer;
  42. import processing.app.legacy.PApplet;
  43. import processing.app.tools.DoubleQuotedArgumentsOnWindowsCommandLine;
  44. import java.io.*;
  45. import java.nio.file.Files;
  46. import java.nio.file.Path;
  47. import java.nio.file.Paths;
  48. import java.nio.file.StandardCopyOption;
  49. import java.util.ArrayList;
  50. import java.util.Collections;
  51. import java.util.List;
  52. import java.util.Map;
  53. import java.util.regex.Pattern;
  54. import java.util.stream.Collectors;
  55. import java.util.stream.Stream;
  56. import static processing.app.I18n.tr;
  57. public class Compiler implements MessageConsumer {
  58. //used by transifex integration
  59. static {
  60. tr("'arch' folder is no longer supported! See http://goo.gl/gfFJzU for more information");
  61. tr("Archiving built core (caching) in: {0}");
  62. tr("Board {0} (platform {1}, package {2}) is unknown");
  63. tr("Bootloader file specified but missing: {0}");
  64. tr("Build options changed, rebuilding all");
  65. tr("Unable to find {0} in {1}");
  66. tr("Invalid quoting: no closing [{0}] char found.");
  67. tr("(legacy)");
  68. tr("Multiple libraries were found for \"{0}\"");
  69. tr(" Not used: {0}");
  70. tr(" Used: {0}");
  71. tr("Library can't use both 'src' and 'utility' folders. Double check {0}");
  72. tr("WARNING: library {0} claims to run on {1} architecture(s) and may be incompatible with your current board which runs on {2} architecture(s).");
  73. tr("Looking for recipes like {0}*{1}");
  74. tr("Board {0}:{1}:{2} doesn''t define a ''build.board'' preference. Auto-set to: {3}");
  75. tr("Selected board depends on '{0}' core (not installed).");
  76. tr("{0} must be a folder");
  77. tr("{0}: Unknown package");
  78. tr("{0} pattern is missing");
  79. tr("Platform {0} (package {1}) is unknown");
  80. tr("Progress {0}");
  81. tr("Missing '{0}' from library in {1}");
  82. tr("Running: {0}");
  83. tr("Running recipe: {0}");
  84. tr("Setting build path to {0}");
  85. tr("Unhandled type {0} in context key {1}");
  86. tr("Unknown sketch file extension: {0}");
  87. tr("Using library {0} at version {1} in folder: {2} {3}");
  88. tr("Using library {0} in folder: {1} {2}");
  89. tr("Using previously compiled file: {0}");
  90. tr("WARNING: Category '{0}' in library {1} is not valid. Setting to '{2}'");
  91. tr("Warning: platform.txt from core '{0}' misses property '{1}', using default value '{2}'. Consider upgrading this core.");
  92. tr("Warning: platform.txt from core '{0}' contains deprecated {1}, automatically converted to {2}. Consider upgrading this core.");
  93. tr("WARNING: Spurious {0} folder in '{1}' library");
  94. tr("Sketch uses {0} bytes ({2}%%) of program storage space. Maximum is {1} bytes.");
  95. tr("Couldn't determine program size: {0}");
  96. tr("Global variables use {0} bytes ({2}%%) of dynamic memory, leaving {3} bytes for local variables. Maximum is {1} bytes.");
  97. tr("Global variables use {0} bytes of dynamic memory.");
  98. tr("Sketch too big; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing it.");
  99. tr("Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint.");
  100. tr("Low memory available, stability problems may occur.");
  101. tr("An error occurred while verifying the sketch");
  102. tr("An error occurred while verifying/uploading the sketch");
  103. tr("Can't find the sketch in the specified path");
  104. tr("Done compiling");
  105. tr("Done uploading");
  106. tr("Error while uploading");
  107. tr("Error while verifying");
  108. tr("Error while verifying/uploading");
  109. tr("Mode not supported");
  110. tr("Multiple files not supported");
  111. tr("No command line parameters found");
  112. tr("No parameters");
  113. tr("No sketch");
  114. tr("No sketchbook");
  115. tr("Only --verify, --upload or --get-pref are supported");
  116. tr("Sketchbook path not defined");
  117. tr("The --upload option supports only one file at a time");
  118. tr("Verifying and uploading...");
  119. }
  120. enum BuilderAction {
  121. COMPILE("-compile"), DUMP_PREFS("-dump-prefs");
  122. final String value;
  123. BuilderAction(String value) {
  124. this.value = value;
  125. }
  126. }
  127. private static final Pattern ERROR_FORMAT = Pattern.compile("(.+\\.\\w+):(\\d+)(:\\d+)*:\\s*((fatal)?\\s*error:\\s*)(.*)\\s*", Pattern.MULTILINE | Pattern.DOTALL);
  128. private final File pathToSketch;
  129. private final Sketch sketch;
  130. private String buildPath;
  131. private File buildCache;
  132. private final boolean verbose;
  133. private RunnerException exception;
  134. public Compiler(Sketch data) {
  135. this(data.getPrimaryFile().getFile(), data);
  136. }
  137. public Compiler(File pathToSketch, Sketch sketch) {
  138. this.pathToSketch = pathToSketch;
  139. this.sketch = sketch;
  140. this.verbose = PreferencesData.getBoolean("build.verbose");
  141. }
  142. public String build(CompilerProgressListener progListener, boolean exportHex) throws RunnerException, PreferencesMapException, IOException {
  143. List<CompilerProgressListener> listeners = new ArrayList<>();
  144. listeners.add(progListener);
  145. return this.build(listeners, exportHex);
  146. }
  147. public String build(List<CompilerProgressListener> progListeners, boolean exportHex) throws RunnerException, PreferencesMapException, IOException {
  148. this.buildPath = sketch.getBuildPath().getAbsolutePath();
  149. this.buildCache = BaseNoGui.getCachePath();
  150. TargetBoard board = BaseNoGui.getTargetBoard();
  151. if (board == null) {
  152. throw new RunnerException("Board is not selected");
  153. }
  154. TargetPlatform platform = board.getContainerPlatform();
  155. TargetPackage aPackage = platform.getContainerPackage();
  156. String vidpid = VIDPID();
  157. PreferencesMap prefs = loadPreferences(board, platform, aPackage, vidpid);
  158. MessageConsumerOutputStream out = new MessageConsumerOutputStream(new ProgressAwareMessageConsumer(new I18NAwareMessageConsumer(System.out, System.err), progListeners), "\n");
  159. MessageConsumerOutputStream err = new MessageConsumerOutputStream(new I18NAwareMessageConsumer(System.err, Compiler.this), "\n");
  160. callArduinoBuilder(board, platform, aPackage, vidpid, BuilderAction.COMPILE, out, err);
  161. out.flush();
  162. err.flush();
  163. if (exportHex) {
  164. runActions("hooks.savehex.presavehex", prefs);
  165. saveHex(prefs);
  166. runActions("hooks.savehex.postsavehex", prefs);
  167. }
  168. return sketch.getPrimaryFile().getFileName();
  169. }
  170. private String VIDPID() {
  171. BoardPort boardPort = BaseNoGui.getDiscoveryManager().find(PreferencesData.get("serial.port"));
  172. if (boardPort == null) {
  173. return "";
  174. }
  175. String vid = boardPort.getPrefs().get("vid");
  176. String pid = boardPort.getPrefs().get("pid");
  177. if (StringUtils.isEmpty(vid) || StringUtils.isEmpty(pid)) {
  178. return "";
  179. }
  180. return vid.toUpperCase() + "_" + pid.toUpperCase();
  181. }
  182. private PreferencesMap loadPreferences(TargetBoard board, TargetPlatform platform, TargetPackage aPackage, String vidpid) throws RunnerException, IOException {
  183. ByteArrayOutputStream stdout = new ByteArrayOutputStream();
  184. ByteArrayOutputStream stderr = new ByteArrayOutputStream();
  185. MessageConsumerOutputStream err = new MessageConsumerOutputStream(new I18NAwareMessageConsumer(new PrintStream(stderr), Compiler.this), "\n");
  186. try {
  187. callArduinoBuilder(board, platform, aPackage, vidpid, BuilderAction.DUMP_PREFS, stdout, err);
  188. } catch (RunnerException e) {
  189. System.err.println(new String(stderr.toByteArray()));
  190. throw e;
  191. }
  192. PreferencesMap prefs = new PreferencesMap();
  193. prefs.load(new ByteArrayInputStream(stdout.toByteArray()));
  194. return prefs;
  195. }
  196. private void addPathFlagIfPathExists(List<String> cmd, String flag, File folder) {
  197. if (folder.exists()) {
  198. cmd.add(flag);
  199. cmd.add(folder.getAbsolutePath());
  200. }
  201. }
  202. private void callArduinoBuilder(TargetBoard board, TargetPlatform platform, TargetPackage aPackage, String vidpid, BuilderAction action, OutputStream outStream, OutputStream errStream) throws RunnerException {
  203. List<String> cmd = new ArrayList<>();
  204. cmd.add(BaseNoGui.getContentFile("arduino-builder").getAbsolutePath());
  205. cmd.add(action.value);
  206. cmd.add("-logger=machine");
  207. File installedPackagesFolder = new File(BaseNoGui.getSettingsFolder(), "packages");
  208. addPathFlagIfPathExists(cmd, "-hardware", BaseNoGui.getHardwareFolder());
  209. addPathFlagIfPathExists(cmd, "-hardware", installedPackagesFolder);
  210. addPathFlagIfPathExists(cmd, "-hardware", BaseNoGui.getSketchbookHardwareFolder());
  211. addPathFlagIfPathExists(cmd, "-tools", BaseNoGui.getContentFile("tools-builder"));
  212. addPathFlagIfPathExists(cmd, "-tools", Paths.get(BaseNoGui.getHardwarePath(), "tools", "avr").toFile());
  213. addPathFlagIfPathExists(cmd, "-tools", installedPackagesFolder);
  214. addPathFlagIfPathExists(cmd, "-built-in-libraries", BaseNoGui.getContentFile("libraries"));
  215. addPathFlagIfPathExists(cmd, "-libraries", BaseNoGui.getSketchbookLibrariesFolder().folder);
  216. String fqbn = Stream.of(aPackage.getId(), platform.getId(), board.getId(), boardOptions(board)).filter(s -> !s.isEmpty()).collect(Collectors.joining(":"));
  217. cmd.add("-fqbn=" + fqbn);
  218. if (!"".equals(vidpid)) {
  219. cmd.add("-vid-pid=" + vidpid);
  220. }
  221. cmd.add("-ide-version=" + BaseNoGui.REVISION);
  222. cmd.add("-build-path");
  223. cmd.add(buildPath);
  224. cmd.add("-warnings=" + PreferencesData.get("compiler.warning_level"));
  225. if (PreferencesData.getBoolean("compiler.cache_core") == true && buildCache != null) {
  226. cmd.add("-build-cache");
  227. cmd.add(buildCache.getAbsolutePath());
  228. }
  229. PreferencesData.getMap()
  230. .subTree("runtime.build_properties_custom")
  231. .entrySet()
  232. .stream()
  233. .forEach(kv -> cmd.add("-prefs=" + kv.getKey() + "=" + kv.getValue()));
  234. cmd.add("-prefs=build.warn_data_percentage=" + PreferencesData.get("build.warn_data_percentage"));
  235. for (Map.Entry<String, String> entry : BaseNoGui.getBoardPreferences().entrySet()) {
  236. if (entry.getKey().startsWith("runtime.tools")) {
  237. cmd.add("-prefs=" + entry.getKey() + "=" + entry.getValue());
  238. }
  239. }
  240. //commandLine.addArgument("-debug-level=10", false);
  241. if (verbose) {
  242. cmd.add("-verbose");
  243. }
  244. cmd.add(pathToSketch.getAbsolutePath());
  245. if (verbose) {
  246. System.out.println(StringUtils.join(cmd, ' '));
  247. }
  248. int result;
  249. try {
  250. Process proc = ProcessUtils.exec(cmd.toArray(new String[0]));
  251. MessageSiphon in = new MessageSiphon(proc.getInputStream(), (msg) -> {
  252. try {
  253. outStream.write(msg.getBytes());
  254. } catch (Exception e) {
  255. exception = new RunnerException(e);
  256. }
  257. });
  258. MessageSiphon err = new MessageSiphon(proc.getErrorStream(), (msg) -> {
  259. try {
  260. errStream.write(msg.getBytes());
  261. } catch (Exception e) {
  262. exception = new RunnerException(e);
  263. }
  264. });
  265. in.join();
  266. err.join();
  267. result = proc.waitFor();
  268. } catch (Exception e) {
  269. throw new RunnerException(e);
  270. }
  271. if (exception != null)
  272. throw exception;
  273. if (result > 1) {
  274. System.err.println(I18n.format(tr("{0} returned {1}"), cmd.get(0), result));
  275. }
  276. if (result != 0) {
  277. RunnerException re = new RunnerException(I18n.format(tr("Error compiling for board {0}."), board.getName()));
  278. re.hideStackTrace();
  279. throw re;
  280. }
  281. }
  282. private void saveHex(PreferencesMap prefs) throws RunnerException {
  283. List<String> compiledSketches = new ArrayList<>(prefs.subTree("recipe.output.tmp_file", 1).values());
  284. List<String> copyOfCompiledSketches = new ArrayList<>(prefs.subTree("recipe.output.save_file", 1).values());
  285. if (isExportCompiledSketchSupported(compiledSketches, copyOfCompiledSketches, prefs)) {
  286. System.err.println(tr("Warning: This core does not support exporting sketches. Please consider upgrading it or contacting its author"));
  287. return;
  288. }
  289. PreferencesMap dict = new PreferencesMap(prefs);
  290. dict.put("ide_version", "" + BaseNoGui.REVISION);
  291. PreferencesMap withBootloaderDict = new PreferencesMap(dict);
  292. dict.put("build.project_name", dict.get("build.project_name") + ".with_bootloader");
  293. if (!compiledSketches.isEmpty()) {
  294. for (int i = 0; i < compiledSketches.size(); i++) {
  295. saveHex(compiledSketches.get(i), copyOfCompiledSketches.get(i), dict);
  296. saveHex(compiledSketches.get(i), copyOfCompiledSketches.get(i), withBootloaderDict);
  297. }
  298. } else {
  299. try {
  300. saveHex(prefs.getOrExcept("recipe.output.tmp_file"), prefs.getOrExcept("recipe.output.save_file"), dict);
  301. saveHex(prefs.getOrExcept("recipe.output.tmp_file"), prefs.getOrExcept("recipe.output.save_file"), withBootloaderDict);
  302. } catch (PreferencesMapException e) {
  303. throw new RunnerException(e);
  304. }
  305. }
  306. }
  307. private void saveHex(String compiledSketch, String copyOfCompiledSketch, PreferencesMap prefs) throws RunnerException {
  308. try {
  309. compiledSketch = StringReplacer.replaceFromMapping(compiledSketch, prefs);
  310. copyOfCompiledSketch = StringReplacer.replaceFromMapping(copyOfCompiledSketch, prefs);
  311. copyOfCompiledSketch = copyOfCompiledSketch.replaceAll(":", "_");
  312. Path compiledSketchPath;
  313. Path compiledSketchPathInSubfolder = Paths.get(prefs.get("build.path"), "sketch", compiledSketch);
  314. Path compiledSketchPathInBuildPath = Paths.get(prefs.get("build.path"), compiledSketch);
  315. if (Files.exists(compiledSketchPathInSubfolder)) {
  316. compiledSketchPath = compiledSketchPathInSubfolder;
  317. } else if (Files.exists(compiledSketchPathInBuildPath)) {
  318. compiledSketchPath = compiledSketchPathInBuildPath;
  319. } else {
  320. return;
  321. }
  322. Path copyOfCompiledSketchFilePath = Paths.get(this.sketch.getFolder().getAbsolutePath(), copyOfCompiledSketch);
  323. Files.copy(compiledSketchPath, copyOfCompiledSketchFilePath, StandardCopyOption.REPLACE_EXISTING);
  324. } catch (IOException e) {
  325. throw new RunnerException(e);
  326. }
  327. }
  328. private boolean isExportCompiledSketchSupported(List<String> compiledSketches, List<String> copyOfCompiledSketches, PreferencesMap prefs) {
  329. return (compiledSketches.isEmpty() || copyOfCompiledSketches.isEmpty() || copyOfCompiledSketches.size() < compiledSketches.size()) && (!prefs.containsKey("recipe.output.tmp_file") || !prefs.containsKey("recipe.output.save_file"));
  330. }
  331. private void runActions(String recipeClass, PreferencesMap prefs) throws RunnerException, PreferencesMapException {
  332. List<String> patterns = prefs.keySet().stream().filter(key -> key.startsWith("recipe." + recipeClass) && key.endsWith(".pattern")).collect(Collectors.toList());
  333. Collections.sort(patterns);
  334. for (String recipe : patterns) {
  335. runRecipe(recipe, prefs);
  336. }
  337. }
  338. private void runRecipe(String recipe, PreferencesMap prefs) throws RunnerException, PreferencesMapException {
  339. PreferencesMap dict = new PreferencesMap(prefs);
  340. dict.put("ide_version", "" + BaseNoGui.REVISION);
  341. dict.put("sketch_path", sketch.getFolder().getAbsolutePath());
  342. String[] cmdArray;
  343. String cmd = prefs.getOrExcept(recipe);
  344. try {
  345. cmdArray = StringReplacer.formatAndSplit(cmd, dict);
  346. } catch (Exception e) {
  347. throw new RunnerException(e);
  348. }
  349. exec(cmdArray);
  350. }
  351. private void exec(String[] command) throws RunnerException {
  352. // eliminate any empty array entries
  353. List<String> stringList = new ArrayList<>();
  354. for (String string : command) {
  355. string = string.trim();
  356. if (string.length() != 0)
  357. stringList.add(string);
  358. }
  359. command = stringList.toArray(new String[stringList.size()]);
  360. if (command.length == 0)
  361. return;
  362. if (verbose) {
  363. for (String c : command)
  364. System.out.print(c + " ");
  365. System.out.println();
  366. }
  367. DefaultExecutor executor = new DefaultExecutor();
  368. executor.setStreamHandler(new PumpStreamHandler() {
  369. @Override
  370. protected Thread createPump(InputStream is, OutputStream os, boolean closeWhenExhausted) {
  371. final Thread result = new Thread(new MyStreamPumper(is, Compiler.this));
  372. result.setName("MyStreamPumper Thread");
  373. result.setDaemon(true);
  374. return result;
  375. }
  376. });
  377. CommandLine commandLine = new DoubleQuotedArgumentsOnWindowsCommandLine(command[0]);
  378. for (int i = 1; i < command.length; i++) {
  379. commandLine.addArgument(command[i], false);
  380. }
  381. int result;
  382. executor.setExitValues(null);
  383. try {
  384. result = executor.execute(commandLine);
  385. } catch (IOException e) {
  386. RunnerException re = new RunnerException(e.getMessage());
  387. re.hideStackTrace();
  388. throw re;
  389. }
  390. executor.setExitValues(new int[0]);
  391. // an error was queued up by message(), barf this back to compile(),
  392. // which will barf it back to Editor. if you're having trouble
  393. // discerning the imagery, consider how cows regurgitate their food
  394. // to digest it, and the fact that they have five stomaches.
  395. //
  396. //System.out.println("throwing up " + exception);
  397. if (exception != null)
  398. throw exception;
  399. if (result > 1) {
  400. // a failure in the tool (e.g. unable to locate a sub-executable)
  401. System.err
  402. .println(I18n.format(tr("{0} returned {1}"), command[0], result));
  403. }
  404. if (result != 0) {
  405. RunnerException re = new RunnerException(tr("Error compiling."));
  406. re.hideStackTrace();
  407. throw re;
  408. }
  409. }
  410. private String boardOptions(TargetBoard board) {
  411. return board.getMenuIds().stream()
  412. .filter(board::hasMenu)
  413. .filter(menuId -> {
  414. String entry = PreferencesData.get("custom_" + menuId);
  415. return entry != null && entry.startsWith(board.getId());
  416. })
  417. .map(menuId -> {
  418. String entry = PreferencesData.get("custom_" + menuId);
  419. String selectionId = entry.substring(board.getId().length() + 1);
  420. return menuId + "=" + selectionId;
  421. })
  422. .collect(Collectors.joining(","));
  423. }
  424. /**
  425. * Part of the MessageConsumer interface, this is called
  426. * whenever a piece (usually a line) of error message is spewed
  427. * out from the compiler. The errors are parsed for their contents
  428. * and line number, which is then reported back to Editor.
  429. */
  430. @Override
  431. public void message(String s) {
  432. int i;
  433. if (!verbose) {
  434. while ((i = s.indexOf(buildPath + File.separator)) != -1) {
  435. s = s.substring(0, i) + s.substring(i + (buildPath + File.separator).length());
  436. }
  437. }
  438. String[] pieces = PApplet.match(s, ERROR_FORMAT);
  439. if (pieces != null) {
  440. String msg = "";
  441. String filename = pieces[1];
  442. int line = PApplet.parseInt(pieces[2]);
  443. int col = -1;
  444. if (pieces[3] != null) {
  445. col = PApplet.parseInt(pieces[3].substring(1));
  446. }
  447. String errorPrefix = pieces[4];
  448. String error = pieces[6];
  449. if (error.trim().equals("SPI.h: No such file or directory")) {
  450. error = tr("Please import the SPI library from the Sketch > Import Library menu.");
  451. msg = tr("\nAs of Arduino 0019, the Ethernet library depends on the SPI library." +
  452. "\nYou appear to be using it or another library that depends on the SPI library.\n\n");
  453. }
  454. if (error.trim().equals("'BYTE' was not declared in this scope")) {
  455. error = tr("The 'BYTE' keyword is no longer supported.");
  456. msg = tr("\nAs of Arduino 1.0, the 'BYTE' keyword is no longer supported." +
  457. "\nPlease use Serial.write() instead.\n\n");
  458. }
  459. if (error.trim().equals("no matching function for call to 'Server::Server(int)'")) {
  460. error = tr("The Server class has been renamed EthernetServer.");
  461. msg = tr("\nAs of Arduino 1.0, the Server class in the Ethernet library " +
  462. "has been renamed to EthernetServer.\n\n");
  463. }
  464. if (error.trim().equals("no matching function for call to 'Client::Client(byte [4], int)'")) {
  465. error = tr("The Client class has been renamed EthernetClient.");
  466. msg = tr("\nAs of Arduino 1.0, the Client class in the Ethernet library " +
  467. "has been renamed to EthernetClient.\n\n");
  468. }
  469. if (error.trim().equals("'Udp' was not declared in this scope")) {
  470. error = tr("The Udp class has been renamed EthernetUdp.");
  471. msg = tr("\nAs of Arduino 1.0, the Udp class in the Ethernet library " +
  472. "has been renamed to EthernetUdp.\n\n");
  473. }
  474. if (error.trim().equals("'class TwoWire' has no member named 'send'")) {
  475. error = tr("Wire.send() has been renamed Wire.write().");
  476. msg = tr("\nAs of Arduino 1.0, the Wire.send() function was renamed " +
  477. "to Wire.write() for consistency with other libraries.\n\n");
  478. }
  479. if (error.trim().equals("'class TwoWire' has no member named 'receive'")) {
  480. error = tr("Wire.receive() has been renamed Wire.read().");
  481. msg = tr("\nAs of Arduino 1.0, the Wire.receive() function was renamed " +
  482. "to Wire.read() for consistency with other libraries.\n\n");
  483. }
  484. if (error.trim().equals("'Mouse' was not declared in this scope")) {
  485. error = tr("'Mouse' not found. Does your sketch include the line '#include <Mouse.h>'?");
  486. //msg = _("\nThe 'Mouse' class is only supported on the Arduino Leonardo.\n\n");
  487. }
  488. if (error.trim().equals("'Keyboard' was not declared in this scope")) {
  489. error = tr("'Keyboard' not found. Does your sketch include the line '#include <Keyboard.h>'?");
  490. //msg = _("\nThe 'Keyboard' class is only supported on the Arduino Leonardo.\n\n");
  491. }
  492. RunnerException ex = placeException(error, filename, line - 1, col);
  493. if (ex != null) {
  494. String fileName = ex.getCodeFile().getPrettyName();
  495. int lineNum = ex.getCodeLine() + 1;
  496. int colNum = ex.getCodeColumn();
  497. String column = (colNum != -1) ? (":" + colNum) : "";
  498. s = fileName + ":" + lineNum + column + ": " + errorPrefix + error + msg;
  499. }
  500. if (ex != null) {
  501. if (exception == null || exception.getMessage().equals(ex.getMessage())) {
  502. exception = ex;
  503. exception.hideStackTrace();
  504. }
  505. }
  506. }
  507. if (s.contains("undefined reference to `SPIClass::begin()'") &&
  508. s.contains("libraries/Robot_Control")) {
  509. String error = tr("Please import the SPI library from the Sketch > Import Library menu.");
  510. exception = new RunnerException(error);
  511. }
  512. if (s.contains("undefined reference to `Wire'") &&
  513. s.contains("libraries/Robot_Control")) {
  514. String error = tr("Please import the Wire library from the Sketch > Import Library menu.");
  515. exception = new RunnerException(error);
  516. }
  517. System.err.println(s);
  518. }
  519. private RunnerException placeException(String message, String fileName, int line, int col) {
  520. for (SketchFile file : sketch.getFiles()) {
  521. if (new File(fileName).getName().equals(file.getFileName())) {
  522. return new RunnerException(message, file, line, col);
  523. }
  524. }
  525. return null;
  526. }
  527. }