Skip to content
ryan-feeley edited this page Jul 8, 2017 · 22 revisions

The process the Arduino environment (1.8.2) uses to build a sketch. More useful information can be found in the Arduino IDE 1.5 3rd party Hardware specification page.

Overview

A number of things have to happen for your Arduino code to get onto the Arduino board. First, the Arduino environment performs some small transformations to make sure that the code is correct C or C++ (two common programming languages). It then gets passed to a compiler (avr-gcc), which turns the human readable code into machine readable instructions (or object files). Then, your code gets combined with (linked against), the standard Arduino libraries that provide basic functions like digitalWrite() or Serial.print(). The result is a single Intel hex file, which contains the specific bytes that need to be written to the program memory of the chip on the Arduino board. This file is then uploaded to the board: transmitted over the USB or serial connection via the bootloader already on the chip or with external programming hardware.

Multi-file sketches

A sketch can contain multiple files (tabs). To manage them, click on the right-facing arrow just above the scroll bar near the top of the environment. Tabs have one of four extensions: .ino, .c, .cpp, or .h (if you provide any other extension, the period will be converted to an underscore). When your sketch is compiled, all tabs with an .ino extension are concatenated together to form the "main sketch file", which the .ino file whose name matches the sketch directory coming first. Tabs with .c or .cpp extensions are compiled separately. To incorporate tabs with a .h extension, you need to #include it (using "double quotes" not <angle brackets>, since the preprocessor should search for this file in the same directory as the main sketch file).

Transformations to the main sketch file

The Arduino environment performs a few transformations to your main sketch file (the concatenation of all the tabs in the sketch with an .ino extension) before passing it to the avr-gcc compiler. These are required to make the main sketch file valid c or c++ code.

First, #include <Arduino.h> (for versions less than 1.0, #include "WProgram.h") is added to the top of your sketch. This header file (found in <ARDUINO>/hardware/cores/<CORE>/) includes all the definitions needed for the standard Arduino core.

Next, the environment searches for function definitions within your main sketch file and creates declarations (prototypes) for them. These are inserted after any comments or pre-processor statements (#includes or #defines), but before any other statements (including type declarations). This means that if you want to use a custom type as a function argument, you should declare it within a separate header file that you #include with a pre-processor statement so type is available before the prototypes are inserted. Note: this generation isn't perfect: it won't create prototypes for functions that have default argument values, or which are declared within a namespace or class. To work around this, you can provide your own prototypes for such functions; prototypes will not be generated for functions that already have declarations before they are used (typically placed near the top of the file).

Finally, the contents of the current target's main.cxx file are appended to the bottom of your sketch.

No transformations are applied to the .c, .cpp, or .h files in a sketch. Further, if you want to call functions defined in a .c file from a .cpp file (like one generated from your sketch), you'll need to wrap its declarations in an 'extern "C" {}' block that is defined only inside of C++ files.

Targets

The Arduino environment supports multiple target boards with different chips (currently, only AVRs), CPU speeds, or bootloaders. These are defined in a board preferences file. Relevant variables include:

.name: the name to display in the Boards menu

.build.mcu: the microcontroller on the board (normally "atmega8" or "atmega168").

.f_cpu: the clock speed at which the microcontroller operates (normally "16000000L", or, for an ATmega168 running on its internal clock, "8000000L").

.core: which sub-directory of the hardware/cores/ directory to link sketches against (normally "arduino").

Build Process

Sketches are compiled by avr-gcc and avr-g++ according to the variables in the boards.txt file of the selected board's platform.

Note: that in Arduino 0004 and later, build.extension in preferences.txt is unused - the main sketch file is always transformed into a .cpp file.

The include path includes the sketch's directory, the board's variant folder (a sub-directory of hardware/arduino/variants specified in boards.txt), the core folder (e.g. the hardware/arduino/core/arduino/ sub-folder of the Arduino application) and the avr include directory (hardware/tools/avr/avr/include/), as well as any library directories (in hardware/libraries/ and the libraries/ sub-directory of your sketchbook) which contain a header file which is included by the main sketch file.

Note that libraries referenced only by another library (and not the main sketch file) are not placed in the include path or linked against the sketch. All libraries used (even indirectly by another library) must be #included by the main sketch file. [The following is out of date?] Also, libraries can't have arbitrary sub-folders. Instead, a special sub-folder "utility", is searched for .c and .cpp files to link in; the "utility" folder is also added to the included path when compiling the library containing it - but not when compiling other files (e.g. headers in the "utility" folder won't be found if #included from the sketch).

The .c and .cpp files of the target are compiled and output with .o extensions to this directory, as is the main sketch file and any other .c or .cpp files in the sketch and any .c or .cpp files in any libraries which are #included in the sketch.

Before compiling each .c or .cpp file, an attempt is made to reuse the previously compiled .o file, which speeds up the build process. A special .d (dependency) file provides a list of all other files included by the source. The compile step is skipped if the .o and .d files exist and have timestamps newer than the source and all the dependent files. If the source or any dependent file has been modified, or any error occurs verifying the files, the compiler is run normally, writing a new .o & .d file. After a new board is selected from the Tools menu, all .c and .cpp files are rebuilt on the next compile. Note that if you are changing preprocessor #defines in your main sketch, they will not propagate to previously complied .o files whose source files are unchanged. In this situation you can trigger a clean build by closing and re-opening the IDE.

These .o files are then linked together into a static library and the main sketch file is linked against this library. Only the parts of the library needed for your sketch are included in the final .hex file, reducing the size of most sketches.

The .hex file is the final output of the compilation which is then uploaded to the board.

When you "verify/compile" or "upload" a sketch, the .hex file is built in a temporary directory. On Mac or Linux this is the system-wide temporary directory (e.g. /tmp). On Windows it is typically C:/Users/<USER>/AppData/Local/Temp.

If verbose output during compilation is checked in the Preferences dialog (or set directly in preferences.txt file as build.verbose=true), the complete command line of each external command executed as part of the build process will be printed in the editor console.

Upload process

Sketches are uploaded by avrdude. The upload process is also controlled by variables in the boards and main preferences files.

Those in the boards file include:

.upload.protocol: the protocol that avrdude should use to talk to the board (typically "stk500").

.upload.speed: the speed (baud rate) avrdude should use when uploading sketches (typically "19200").

.upload.maximum_size: the maximum size for a sketch on the board (dependent on the chip and the size of the bootloader).

If verbose output during upload is checked in the Preferences dialog (or set directly in preferences.txt file as upload.verbose=true), the complete command line of each external command executed as part of the upload process will be printed in the editor console.