Arduino IDE Warnings And Map Files
This page describes the process and modifications necessary to the Arduino Java source to enable GCC's "-W -Wall" warnings, enable the linker to produce a map file, enable a more detailed code size report, and produce a disassembly listing. The instructions assume a Windows operating system, but the file to be modified and the basic build process are also applicable to Linux or Mac OS-X.
The process is fairly straight forward. The Building Arduino page gives good details on what tools you'll need to have installed to do this (Cygwin, Ant, and a JDK). Run through all the steps to make sure you can do a clean build.
Install Ant into the "C:\Program Files" directory. I use WinZip, so opening the downloaded zip file, typing "C:\Program Files" into the "Extract to" box at the top and clicking "Extract" places them there. If there's a newer version, you'll want to update the arduino.sh script below with the new directory name.
You'll need a JDK to compile the Arduino code. I use the Oracle SE JDK, mostly because I already had the run-time environment installed, and there was no point in changing versions. Any JDK should work, but you'll need to update the arduino.sh script below to reflect whatever directory it's installed into. The version I installed put itself into "C:\Program Files\Java\jdk1.6.0_23". If there's a newer version, again, update the arduino.sh script below to reflect the correct directory.
Now copy the text below in the arduino.sh script below into a text file in your home directory. This sets up the path for the build process to find Ant and the JDK, and to put them in your path. Any text editor (vim, nano, Notepad) will work, but don't use Wordpad.
Code: arduino.sh |
#!/bin/sh export ANT_HOME=/cygdrive/c/Program\ Files/apache-ant-1.8.1 export JAVA_HOME=/cygdrive/c/Program\ Files/Java/jdk1.6.0_23 export PATH=${PATH}:${ANT_HOME}/bin:${JAVA_HOME}/bin |
If you didn't open a Cygwin shell to create the arduino.sh file, open a new shell now. Enter the following command to include the new environment variables in to the shell.
$ . arduino.sh
Follow the instructions in the Building Arduino page to get the Arduino source code with git. Even on a fast connection, this is going to take a while. The Arduino source is somewhere right around 1GB.
Before we build the Arduino source, we'll make a copy of the Blink sketch so we have something to test the changes with.
$ cd ~ $ mkdir Blink $ cp ~/Arduino/build/shared/examples/1.Basics/Blink/* Blink
Once the source has been fetched from git, change into the build directory and build the project:
$ cd ~/Arduino/build $ ant run
After Ant has built the code, it will automatically start a copy of the Arduino IDE. It may take a few seconds to start up as Java does whatever it that Java does that takes so long to do. Now open the Blink sketch that was copied into ~/Blink and compile it to convince yourself everything is working correctly (when navigating to the directory where the Blink sketch is stored with the Arduino IDE, go to C:\cygwin\home\<windows_user_name>\Blink). After compiling the sketch, be sure to exit the Arduino IDE before the next steps.
Now apply the following patches to the ~/Arduino/app/src/processing/app/debug/Compiler.java file. The line numbers below assume the Compiler.java file from the 0021 release. Later releases will be similar, but the line numbers may not line up exactly.
Code: ./app/src/processing/app/debug/Compiler.java |
diff --git a/app/src/processing/app/debug/Compiler.java b/app/src/processing/app/debug/Compiler.java index f2fa5e2..86d15d4 100644 --- a/app/src/processing/app/debug/Compiler.java +++ b/app/src/processing/app/debug/Compiler.java @@ -44,6 +44,7 @@ public class Compiler implements MessageConsumer { String buildPath; String primaryClassName; boolean verbose; + PrintWriter outputFile = null; RunnerException exception; @@ -179,8 +180,25 @@ public class Compiler implements MessageConsumer { baseCommandLinker.add("-L" + buildPath); baseCommandLinker.add("-lm"); + if (Preferences.getBoolean("compiler.map_file")) + baseCommandLinker.add("-Wl,-Map," + sketch.getFolder() + File.separator + sketch.getName() + ".map"); + execAsynchronously(baseCommandLinker); + // 4a. create the size output + if (Preferences.getBoolean("compiler.detailed_size")) { + List baseCommandsize = new ArrayList(Arrays.asList(new String[] { + avrBasePath + "avr-size", + "-C", + "--mcu=" + boardPreferences.get("build.mcu"), + buildPath + File.separator + primaryClassName + ".elf"//, + //" > ", + //sketch.getFolder() + File.separator + sketch.getName() + ".size" + })); + + execAsynchronously(baseCommandsize); + } + List baseCommandObjcopy = new ArrayList(Arrays.asList(new String[] { avrBasePath + "avr-objcopy", "-O", @@ -210,6 +228,18 @@ public class Compiler implements MessageConsumer { commandObjcopy.add(buildPath + File.separator + primaryClassName + ".hex"); execAsynchronously(commandObjcopy); + // 7. produce the .lst file + if (Preferences.getBoolean("compiler.lst_file")) { + List baseCommandObjdump = new ArrayList(Arrays.asList(new String[] { + avrBasePath + "avr-objdump", + "-d", + "-S", + buildPath + File.separator + primaryClassName + ".elf" + })); + + execAsynchronously(baseCommandObjdump, sketch.getFolder() + File.separator + sketch.getName() + ".lst"); + } + return true; } @@ -261,6 +291,14 @@ public class Compiler implements MessageConsumer { * Either succeeds or throws a RunnerException fit for public consumption. */ private void execAsynchronously(List commandList) throws RunnerException { + execAsynchronouslyFile (commandList, null); + } + + private void execAsynchronously(List commandList, String outputFileName) throws RunnerException { + execAsynchronouslyFile (commandList, outputFileName); + } + + private void execAsynchronouslyFile(List commandList, String outputFileName) throws RunnerException { String[] command = new String[commandList.size()]; commandList.toArray(command); int result = 0; @@ -275,6 +313,19 @@ public class Compiler implements MessageConsumer { firstErrorFound = false; // haven't found any errors yet secondErrorFound = false; + if (outputFileName != null) + { + try { + File filename = new File (outputFileName); + FileWriter fout = new FileWriter (filename); + outputFile = new PrintWriter (fout, true); + } catch (IOException e) { + RunnerException re = new RunnerException(e.getMessage()); + re.hideStackTrace(); + throw re; + } + } + Process process; try { @@ -321,9 +372,15 @@ public class Compiler implements MessageConsumer { re.hideStackTrace(); throw re; } + + if (outputFile != null) + { + outputFile.flush (); + outputFile.close (); + outputFile = null; + } } - /** * Part of the MessageConsumer interface, this is called * whenever a piece (usually a line) of error message is spewed @@ -333,49 +390,54 @@ public class Compiler implements MessageConsumer { public void message(String s) { int i; - // remove the build path so people only see the filename - // can't use replaceAll() because the path may have characters in it which - // have meaning in a regular expression. - if (!verbose) { - while ((i = s.indexOf(buildPath + File.separator)) != -1) { - s = s.substring(0, i) + s.substring(i + (buildPath + File.separator).length()); + if (outputFile != null) + outputFile.print (s); + else + { + // remove the build path so people only see the filename + // can't use replaceAll() because the path may have characters in it which + // have meaning in a regular expression. + if (!verbose) { + while ((i = s.indexOf(buildPath + File.separator)) != -1) { + s = s.substring(0, i) + s.substring(i + (buildPath + File.separator).length()); + } } - } - - // look for error line, which contains file name, line number, - // and at least the first line of the error message - String errorFormat = "([\\w\\d_]+.\\w+):(\\d+):\\s*error:\\s*(.*)\\s*"; - String[] pieces = PApplet.match(s, errorFormat); + + // look for error line, which contains file name, line number, + // and at least the first line of the error message + String errorFormat = "([\\w\\d_]+.\\w+):(\\d+):\\s*error:\\s*(.*)\\s*"; + String[] pieces = PApplet.match(s, errorFormat); // if (pieces != null && exception == null) { // exception = sketch.placeException(pieces[3], pieces[1], PApplet.parseInt(pieces[2]) - 1); // if (exception != null) exception.hideStackTrace(); // } - - if (pieces != null) { - RunnerException e = sketch.placeException(pieces[3], pieces[1], PApplet.parseInt(pieces[2]) - 1); - - // replace full file path with the name of the sketch tab (unless we're - // in verbose mode, in which case don't modify the compiler output) - if (e != null && !verbose) { - SketchCode code = sketch.getCode(e.getCodeIndex()); - String fileName = code.isExtension(sketch.getDefaultExtension()) ? code.getPrettyName() : code.getFileName(); - s = fileName + ":" + e.getCodeLine() + ": error: " + e.getMessage(); + + if (pieces != null) { + RunnerException e = sketch.placeException(pieces[3], pieces[1], PApplet.parseInt(pieces[2]) - 1); + + // replace full file path with the name of the sketch tab (unless we're + // in verbose mode, in which case don't modify the compiler output) + if (e != null && !verbose) { + SketchCode code = sketch.getCode(e.getCodeIndex()); + String fileName = code.isExtension(sketch.getDefaultExtension()) ? code.getPrettyName() : code.getFileName(); + s = fileName + ":" + e.getCodeLine() + ": error: " + e.getMessage(); + } + + if (pieces[3].trim().equals("SPI.h: No such file or directory")) { + e = new RunnerException("Please import the SPI library from the Sketch > Import Library menu."); + s += "\nAs of Arduino 0019, the Ethernet library depends on the SPI library." + + "\nYou appear to be using it or another library that depends on the SPI library."; + } + + if (exception == null && e != null) { + exception = e; + exception.hideStackTrace(); + } } - - if (pieces[3].trim().equals("SPI.h: No such file or directory")) { - e = new RunnerException("Please import the SPI library from the Sketch > Import Library menu."); - s += "\nAs of Arduino 0019, the Ethernet library depends on the SPI library." + - "\nYou appear to be using it or another library that depends on the SPI library."; - } - - if (exception == null && e != null) { - exception = e; - exception.hideStackTrace(); - } + + System.err.print(s); } - - System.err.print(s); } ///////////////////////////////////////////////////////////////////////////// @@ -411,13 +473,19 @@ public class Compiler implements MessageConsumer { "-c", // compile, don't link "-g", // include debugging info (so errors include line numbers) "-Os", // optimize for size - "-w", // surpress all warnings "-ffunction-sections", // place each function in its own section "-fdata-sections", "-mmcu=" + boardPreferences.get("build.mcu"), "-DF_CPU=" + boardPreferences.get("build.f_cpu"), "-DARDUINO=" + Base.REVISION, })); + + if (!Preferences.getBoolean("compiler.show_all_warnings")) + baseCommandCompiler.add("-w"); // suppress all warnings + else { + baseCommandCompiler.add("-W"); // show all warnings + baseCommandCompiler.add("-Wall"); // be aggressive with warnings + } for (int i = 0; i < includePaths.size(); i++) { baseCommandCompiler.add("-I" + (String) includePaths.get(i)); @@ -439,7 +507,6 @@ public class Compiler implements MessageConsumer { "-c", // compile, don't link "-g", // include debugging info (so errors include line numbers) "-Os", // optimize for size - "-w", // surpress all warnings "-fno-exceptions", "-ffunction-sections", // place each function in its own section "-fdata-sections", @@ -448,6 +515,13 @@ public class Compiler implements MessageConsumer { "-DARDUINO=" + Base.REVISION, })); + if (!Preferences.getBoolean("compiler.show_all_warnings")) + baseCommandCompilerCPP.add("-w"); // suppress all warnings + else { + baseCommandCompilerCPP.add("-W"); // show all warnings + baseCommandCompilerCPP.add("-Wall"); // be aggressive with warnings + } + for (int i = 0; i < includePaths.size(); i++) { baseCommandCompilerCPP.add("-I" + (String) includePaths.get(i)); } |
Change back to the build directory (~/Arduino/build) and rebuild. After the IDE opens again, recompile the Blink sketch. It should recompile exactly as before.
To enable the various options, the 'preferences.txt' file in the Arduino sketch directory needs to be created. There are a bunch of options available that are very poorly documented. Supposedly there is some documentation in the forums, but certainly none in the [www.arduino.cc/en/Hacking/Preferences] page. Normally, under Windows, the Arduino directory will be in the '/Documents And Settings/<user>/Application Data/Arduino' folder of the current user. Exit the Arduino IDE, then add these lines to the end of 'preferences.txt' to enable the various options:
Code: preferences.txt |
compiler.detailed_size=true compiler.lst_file=true compiler.map_file=true compiler.show_all_warnings=true |
Restart the Arduino IDE with using 'ant run', and load the Blink sketch. Compile the sketch, and now a few dozen lines of ugly red warnings should be emitted. Apparently, Arduino code does not compile clean, by nature. I do not approve of this, as I think ignoring compiler generated warnings encourages bad programming practices, and often indicate an error that will cause grief down the road (such as signed vs unsigned numbers, size issues, missing parameters, etc).
In the IDE output window, there should be a more detailed size of the program. Examine the ~/Blink directory, and there should be a 'Blink.map' and a 'Blink.lst' file present. If so, everything has gone grandly. If not, better figure out what went wrong :)
If the Arduino IDE has already been installed on the system, locate the installation directory (mine is installed into 'C:\Program Files') and replace the pde.jar file with the updated one. The following commands should do it:
$ cd /cygdrive/c/Program\ Files/arduino-0021/lib $ mv pde.jar pde_org.jar $ cp ~/Arduino/x/pde.jar .
Alternatively, a new distribution ZIP file can be built thusly:
$ ant dist
Enter a version number for the changed version. I typically use the current version number, and add the letter 'a' to the end (0021a). This zip file can be found in the 'build/windows' directory, underneath the directory from where the 'ant dist' command was run. This zip file can be installed like any other version of the Arduino IDE, by simply un-zipping it somewhere, and running the 'arduino.exe' file.
This is a list of resources for the packages: