|
| 1 | +# Sketch build profiles and reproducible builds |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Arduino build system and Sketch specifications lack a way to share and archive sketches and guarantee reproducibility of the built firmware at any time in the future as it was last successfully built. |
| 6 | + |
| 7 | +These incompatibilities might arise due to newer versions of a Platform, Tool, or Library. |
| 8 | + |
| 9 | +## Problem |
| 10 | + |
| 11 | +An Arduino project is known as Sketch. It comprises its main `.ino` file plus optional local source code (in the same folder) and an optional extra `src` folder containing additional sources if required. |
| 12 | + |
| 13 | +The sketch compiles against a set of globally installed libraries and platforms. When said library and/or part of the hardware platform is updated, sometimes introducing some breaking changes, that sketch might not compile anymore or behave differently. |
| 14 | + |
| 15 | +Currently, the only way to guarantee portability and reproducibility of a sketch build at a later time is for the developer to provide instructions to the final user for the installation of the sketch dependencies, including the exact versions of the boards platform and libraries. |
| 16 | + |
| 17 | +## Goals |
| 18 | + |
| 19 | +This RFC aims to propose new augmentations to the current library management and a build system that can be entirely transparent to the user and retains backward compatibility yet allowing more flexibility for the future. |
| 20 | + |
| 21 | +The following should be true: |
| 22 | + |
| 23 | +- Implement ways for any Arduino Sketch to behave as a reproducible build with an option for versioning |
| 24 | + |
| 25 | +- Allow sharing of such sketch and “environment settings” from |
| 26 | + |
| 27 | + - IDE 1.8 and 2.0 using Archive Sketch |
| 28 | + - Arduino Cloud using Share/Download Sketch |
| 29 | + |
| 30 | +## Constraints |
| 31 | + |
| 32 | +- Must be compatible across build systems |
| 33 | + |
| 34 | + - Arduino CLI |
| 35 | + - Arduino IDE 2.0 |
| 36 | + - Arduino IDE 1.8 |
| 37 | + - Arduino Cloud |
| 38 | + |
| 39 | +Must not break pre-existing setups or be completely transparent to / ignored by previous versions of the tools |
| 40 | + |
| 41 | +## Proposed Solution |
| 42 | + |
| 43 | +The proposal is to add a project file called `sketch.yaml`. This file will be in YAML format and it will contain a description of all the resources required to compile a sketch, in particular: |
| 44 | + |
| 45 | +- the board FQBN |
| 46 | +- the target core platform version (and the 3rd party platform index URL if needed) |
| 47 | +- a possible core platform that is a dependency of the target core platform (again with version and index URL) |
| 48 | +- the libraries used (including their version) |
| 49 | + |
| 50 | +We will call the set of the information above a **profile**. Each profile will be saved in the file `sketch.yaml` and this file may have one or more profiles. |
| 51 | + |
| 52 | +The format of the file is the following: |
| 53 | + |
| 54 | +``` |
| 55 | +profiles: |
| 56 | + <PROFILE_NAME>: |
| 57 | + notes: <USER_NOTES> |
| 58 | + fqbn: <FQBN> |
| 59 | + platforms: |
| 60 | + - platform: <PLATFORM> (<PLATFORM_VERSION>) |
| 61 | + platform_index_url: <3RD_PARTY_PLATFORM_URL> |
| 62 | + - platform: <PLATFORM_DEPENDENCY> (<PLATFORM_DEPENDENCY_VERSION>) |
| 63 | + platform_index_url: <3RD_PARTY_PLATFORM_DEPENDENCY_URL> |
| 64 | + libraries: |
| 65 | + - <LIB_NAME> (<LIB_VERSION>) |
| 66 | + - <LIB_NAME> (<LIB_VERSION>) |
| 67 | + - <LIB_NAME> (<LIB_VERSION>) |
| 68 | +
|
| 69 | +...more profiles here... |
| 70 | +
|
| 71 | +default_profile: <DEFAULT_PROFILE_NAME> |
| 72 | +``` |
| 73 | + |
| 74 | +We have a `profiles:` section containing all the profiles. Each field is self-explanatory, in particular: |
| 75 | + |
| 76 | +- `<PROFILE_NAME>` is the profile identifier, it’s a user-defined field, and the allowed characters are alphanumerics, underscore `_`, dot `.`, and dash `-` |
| 77 | +- `<PLATFORM>` is the target core platform identifier, for example, `arduino:avr` or `adafruit:samd` |
| 78 | +- `<PLATFORM_VERSION>` is the target core platform version required |
| 79 | +- `<3RD_PARTY_PLATFORM_URL>` is the index URL to download the target core platform (also known as “Additional Boards Manager URLs” in the Arduino IDE). This field can be omitted for the official `arduino:*` platforms. |
| 80 | +- `<PLATFORM_DEPENDENCY>`, `<PLATFORM_DEPENDENCY_VERSION>`, and `<3RD_PARTY_PLATFORM_DEPENDENCY_URL>` contains the same information as `<PLATFORM>`, `<PLATFORM_VERSION>`, and `<3RD_PARTY_PLATFORM_URL>` but for the core platform dependency of the main core platform. These fields are optional. |
| 81 | +- `<LIB_VERSION>` is the version required for the library, for example, `1.0.0` |
| 82 | +- `<USER_NOTES>` is a free text string available to the developer to add comments |
| 83 | +- `<DEFAULT_PROFILE_NAME>` is the profile used by default (more on that later) |
| 84 | + |
| 85 | +A complete example of a `sketch.yaml` may be the following: |
| 86 | + |
| 87 | +``` |
| 88 | +profiles: |
| 89 | + nanorp: |
| 90 | + fqbn: arduino:mbed_nano:nanorp2040connect |
| 91 | + platforms: |
| 92 | + - platform: arduino:mbed_nano (2.1.0) |
| 93 | + libraries: |
| 94 | + - ArduinoIoTCloud (1.0.2) |
| 95 | + - Arduino_ConnectionHandler (0.6.4) |
| 96 | + - TinyDHT sensor library (1.1.0) |
| 97 | +
|
| 98 | + another_profile_name: |
| 99 | + notes: testing the limit of the AVR platform, may be unstable |
| 100 | + fqbn: arduino:avr:uno |
| 101 | + platforms: |
| 102 | + - platform: arduino:avr (1.8.4) |
| 103 | + libraries: |
| 104 | + - VitconMQTT (1.0.1) |
| 105 | + - Arduino_ConnectionHandler (0.6.4) |
| 106 | + - TinyDHT sensor library (1.1.0) |
| 107 | +
|
| 108 | + tiny: |
| 109 | + notes: testing the very limit of the AVR platform, it will be very unstable |
| 110 | + fqbn: attiny:avr:ATtinyX5:cpu=attiny85,clock=internal16 |
| 111 | + platforms: |
| 112 | + - platform: attiny:[email protected] |
| 113 | + platform_index_url: https://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json |
| 114 | + - platform: arduino:[email protected] |
| 115 | + libraries: |
| 116 | + - ArduinoIoTCloud (1.0.2) |
| 117 | + - Arduino_ConnectionHandler (0.6.4) |
| 118 | + - TinyDHT sensor library (1.1.0) |
| 119 | +
|
| 120 | + feather: |
| 121 | + fqbn: adafruit:samd:adafruit_feather_m0 |
| 122 | + platforms: |
| 123 | + - platform: adafruit:samd (1.6.0) |
| 124 | + platform_index_url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json |
| 125 | + libraries: |
| 126 | + - ArduinoIoTCloud (1.0.2) |
| 127 | + - Arduino_ConnectionHandler (0.6.4) |
| 128 | + - TinyDHT sensor library (1.1.0) |
| 129 | +
|
| 130 | +default_profile: nanorp |
| 131 | +``` |
| 132 | + |
| 133 | +## Building a sketch |
| 134 | + |
| 135 | +When a `sketch.yaml` file exists in the sketch, it can be leveraged to compile the sketch with the `--profile` flag in the compile command: |
| 136 | + |
| 137 | +``` |
| 138 | +arduino-cli compile [sketch] --profile nanorp |
| 139 | +``` |
| 140 | + |
| 141 | +In this case, the sketch will be compiled using the core platform and libraries specified in the `nanorp` profile. If a core platform or a library is missing it will be automatically downloaded and installed on the fly in a dedicated directory inside the data folder. The dedicated storage is not accessible to the user and is meant as a “cache” of the resources used to build the sketch. |
| 142 | + |
| 143 | +When using the profile-based build, the globally installed platforms and libraries are excluded from the compile and **can not be used in any way**, in other words, the build is isolated from the system and will rely only on the resources specified in the profile: this will ensure that the build is portable and reproducible independently from the platforms and libraries installed in the system. |
| 144 | + |
| 145 | +## Using a default profile |
| 146 | + |
| 147 | +If a `default_profile` is specified in the `sketch.yaml` then the “classic” compile command: |
| 148 | + |
| 149 | +``` |
| 150 | +arduino-cli compile [sketch] |
| 151 | +``` |
| 152 | + |
| 153 | +will, instead, trigger a profile-based build using the default profile indicated in the `sketch.yaml`. |
| 154 | + |
| 155 | +## Vendoring libraries with custom modifications |
| 156 | + |
| 157 | +We will add the possibility to add custom libraries directly in the sketch using the `libraries` subdirectory in the sketch root folder. |
| 158 | + |
| 159 | +A typical usage scenario is when the sketch needs a library that is not offered for installation from Library Manager, or if the sketch needs a library with some customizations that are not available in the upstream release. |
| 160 | + |
| 161 | +To accomplish this the idea is to put the custom libraries' source code directly in the `libraries` subdirectory of the sketch (exactly as we do for the globally installed libraries in the `libraries` subdirectory of the sketchbook root folder). For example: |
| 162 | + |
| 163 | +``` |
| 164 | +~/Arduino/HumiditySensor$ tree |
| 165 | +HumiditySensor |
| 166 | +├── HumiditySensor.ino |
| 167 | +├── sketch.yaml |
| 168 | +└── libraries |
| 169 | + ├── DHTSensorCustom |
| 170 | + │ ├── library.properties |
| 171 | + │ ├── README |
| 172 | + │ └── src |
| 173 | + │ ├── DHT.cpp |
| 174 | + │ ├── DHT.h |
| 175 | + │ └── DHTSensorCustom.h |
| 176 | + └── NTPClientEth |
| 177 | + ├── NTPClient.h |
| 178 | + └── NTPClient.cpp |
| 179 | +``` |
| 180 | + |
| 181 | +If we run a “classic” build, without profiles, then the libraries `DHTSensorCustom` and `NTPClientEth` will be included at compile time as they were installed globally. |
| 182 | + |
| 183 | +If we want to run a reproducible build with profiles, we need to explicitly specify the bundled libraries we intend to use in the `sketch.yaml` in this way: |
| 184 | + |
| 185 | +``` |
| 186 | +profiles: |
| 187 | + nanorp: |
| 188 | + fqbn: arduino:mbed_nano:nanorp2040connect |
| 189 | + platforms: |
| 190 | + - platform: arduino:mbed_nano (2.1.0) |
| 191 | + libraries: |
| 192 | + - ArduinoIoTCloud (1.0.2) |
| 193 | + - Arduino_ConnectionHandler (0.6.4) |
| 194 | + - TinyDHT sensor library (1.1.0) |
| 195 | + vendored_libraries: |
| 196 | + - NTPClientEth |
| 197 | +
|
| 198 | + uno: |
| 199 | + notes: testing the limit of the AVR platform, may be unstable |
| 200 | + fqbn: arduino:avr:uno |
| 201 | + platforms: |
| 202 | + - platform: arduino:avr (1.8.4) |
| 203 | + libraries: |
| 204 | + - VitconMQTT (1.0.1) |
| 205 | + - Arduino_ConnectionHandler (0.6.4) |
| 206 | + vendored_libraries: |
| 207 | + - DHTSensorCustom |
| 208 | + - NTPClientEth |
| 209 | +``` |
| 210 | + |
| 211 | +In this case: |
| 212 | + |
| 213 | +- running a build using the profile `nanorp` will use the bundled `NTPClientEth` library, but not `DHTSensorCustom` |
| 214 | +- running a build with the profile `uno` will use both `NTPClientEth` and `DHTSensorCustom` |
| 215 | + |
| 216 | +This behavior is required to allow different profiles to use a different set of vendored libraries. |
0 commit comments