Skip to content

Commit 2173c12

Browse files
Fix loading platforms with malformed boards.txt (#1326)
* Fix loading platforms with malformed boards.txt * [skip changelog] Fix legacy tests * [skip changelog] Remove unecessary sentence from documentation Co-authored-by: per1234 <[email protected]> * [skip changelog] Fix some docs and comments * Fix board skipping logic * [skip changelog] Fix unit tests * Fix panic if a board has a property that starts with menu Co-authored-by: per1234 <[email protected]>
1 parent 7d59776 commit 2173c12

File tree

6 files changed

+149
-10
lines changed

6 files changed

+149
-10
lines changed

Diff for: arduino/cores/packagemanager/loader.go

+41-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020
"os"
2121
"path/filepath"
22+
"strings"
2223

2324
"github.com/arduino/arduino-cli/arduino/cores"
2425
"github.com/arduino/arduino-cli/configuration"
@@ -250,8 +251,7 @@ func (pm *PackageManager) loadPlatform(targetPackage *cores.Package, platformPat
250251
pm.Log.Infof("Package is built-in")
251252
}
252253
if err := pm.loadPlatformRelease(release, platformPath); err != nil {
253-
return status.Newf(codes.FailedPrecondition, "loading platform release: %s", err)
254-
254+
return status.Newf(codes.FailedPrecondition, "loading platform release %s: %s", release, err)
255255
}
256256
pm.Log.WithField("platform", release).Infof("Loaded platform")
257257

@@ -279,7 +279,7 @@ func (pm *PackageManager) loadPlatform(targetPackage *cores.Package, platformPat
279279
}
280280
release := platform.GetOrCreateRelease(version)
281281
if err := pm.loadPlatformRelease(release, versionDir); err != nil {
282-
return status.Newf(codes.FailedPrecondition, "loading platform release: %s", err)
282+
return status.Newf(codes.FailedPrecondition, "loading platform release %s: %s", release, err)
283283
}
284284
pm.Log.WithField("platform", release).Infof("Loaded platform")
285285
}
@@ -341,7 +341,7 @@ func (pm *PackageManager) loadPlatformRelease(platform *cores.PlatformRelease, p
341341
}
342342

343343
if err := pm.loadBoards(platform); err != nil {
344-
return err
344+
return fmt.Errorf("loading boards: %s", err)
345345
}
346346

347347
return nil
@@ -374,14 +374,47 @@ func (pm *PackageManager) loadBoards(platform *cores.PlatformRelease) error {
374374

375375
propertiesByBoard := boardsProperties.FirstLevelOf()
376376

377-
platform.Menus = propertiesByBoard["menu"]
377+
if menus, ok := propertiesByBoard["menu"]; ok {
378+
platform.Menus = menus
379+
} else {
380+
platform.Menus = properties.NewMap()
381+
}
378382

379-
delete(propertiesByBoard, "menu") // TODO: check this one
383+
// This is not a board id so we remove it to correctly
384+
// set all other boards properties
385+
delete(propertiesByBoard, "menu")
380386

387+
skippedBoards := []string{}
381388
for boardID, boardProperties := range propertiesByBoard {
382-
boardProperties.Set("_id", boardID) // TODO: What is that for??
383-
board := platform.GetOrCreateBoard(boardID)
389+
var board *cores.Board
390+
for key := range boardProperties.AsMap() {
391+
if !strings.HasPrefix(key, "menu.") {
392+
continue
393+
}
394+
// Menu keys are formed like this:
395+
// menu.cache.off=false
396+
// menu.cache.on=true
397+
// so we assume that the a second element in the slice exists
398+
menuName := strings.Split(key, ".")[1]
399+
if !platform.Menus.ContainsKey(menuName) {
400+
fqbn := fmt.Sprintf("%s:%s:%s", platform.Platform.Package.Name, platform.Platform.Architecture, boardID)
401+
skippedBoards = append(skippedBoards, fqbn)
402+
goto next_board
403+
}
404+
}
405+
// The board's ID must be available in a board's properties since it can
406+
// be used in all configuration files for several reasons, like setting compilation
407+
// flags depending on the board id.
408+
// For more info:
409+
// https://arduino.github.io/arduino-cli/dev/platform-specification/#global-predefined-properties
410+
boardProperties.Set("_id", boardID)
411+
board = platform.GetOrCreateBoard(boardID)
384412
board.Properties.Merge(boardProperties)
413+
next_board:
414+
}
415+
416+
if len(skippedBoards) > 0 {
417+
return fmt.Errorf("skipping loading of boards %s: malformed custom board options", strings.Join(skippedBoards, ", "))
385418
}
386419

387420
return nil

Diff for: arduino/cores/packagemanager/package_manager_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,10 @@ func TestFindToolsRequiredForBoard(t *testing.T) {
228228
loadIndex("https://dl.espressif.com/dl/package_esp32_index.json")
229229
loadIndex("http://arduino.esp8266.com/stable/package_esp8266com_index.json")
230230
loadIndex("https://adafruit.github.io/arduino-board-index/package_adafruit_index.json")
231-
errs := pm.LoadHardware()
232-
require.Len(t, errs, 0)
231+
// We ignore the errors returned since they might not be necessarily blocking
232+
// but just warnings for the user, like in the case a board is not loaded
233+
// because of malformed menus
234+
pm.LoadHardware()
233235
esp32, err := pm.FindBoardWithFQBN("esp32:esp32:esp32")
234236
require.NoError(t, err)
235237
esptool231 := pm.FindToolDependency(&cores.ToolDependency{

Diff for: legacy/builder/test/user_hardware/my_avr_platform/avr/boards.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
menu.cpu=Processor
2+
13
custom_yun.name=Arduino Yún
24
custom_yun.upload.via_ssh=true
35

Diff for: test/test_core.py

+37
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# a commercial license, send an email to [email protected].
1515
import os
1616
import datetime
17+
import shutil
1718
import time
1819
import platform
1920
import pytest
@@ -675,3 +676,39 @@ def test_core_list_platform_without_platform_txt(run_command, data_dir):
675676
core = cores[0]
676677
assert core["id"] == "some-packager:some-arch"
677678
assert core["name"] == "some-packager-some-arch"
679+
680+
681+
def test_core_with_wrong_custom_board_options_is_loaded(run_command, data_dir):
682+
test_platform_name = "platform_with_wrong_custom_board_options"
683+
platform_install_dir = Path(data_dir, "hardware", "arduino-beta-dev", test_platform_name)
684+
platform_install_dir.mkdir(parents=True)
685+
686+
# Install platform in Sketchbook hardware dir
687+
shutil.copytree(
688+
Path(__file__).parent / "testdata" / test_platform_name, platform_install_dir, dirs_exist_ok=True,
689+
)
690+
691+
assert run_command("update")
692+
693+
res = run_command("core list --format json")
694+
assert res.ok
695+
696+
cores = json.loads(res.stdout)
697+
mapped = {core["id"]: core for core in cores}
698+
assert len(mapped) == 1
699+
# Verifies platform is loaded except excluding board with wrong options
700+
assert "arduino-beta-dev:platform_with_wrong_custom_board_options" in mapped
701+
boards = {b["fqbn"]: b for b in mapped["arduino-beta-dev:platform_with_wrong_custom_board_options"]["boards"]}
702+
assert len(boards) == 1
703+
# Verify board with malformed options is not loaded
704+
assert "arduino-beta-dev:platform_with_wrong_custom_board_options:nessuno" not in boards
705+
# Verify other board is loaded
706+
assert "arduino-beta-dev:platform_with_wrong_custom_board_options:altra" in boards
707+
# Verify warning is shown to user
708+
assert (
709+
"Error initializing instance: "
710+
+ "loading platform release arduino-beta-dev:[email protected]: "
711+
+ "loading boards: "
712+
+ "skipping loading of boards arduino-beta-dev:platform_with_wrong_custom_board_options:nessuno: "
713+
+ "malformed custom board options"
714+
) in res.stderr
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
menu.cpu=Processor
2+
3+
nessuno.name=Arduino Nessuno
4+
nessuno.vid.0=0x2341
5+
nessuno.pid.0=0x0043
6+
nessuno.vid.1=0x2341
7+
nessuno.pid.1=0x0001
8+
nessuno.vid.2=0x2A03
9+
nessuno.pid.2=0x0043
10+
nessuno.vid.3=0x2341
11+
nessuno.pid.3=0x0243
12+
nessuno.upload.tool=avrdude
13+
nessuno.upload.protocol=arduino
14+
nessuno.upload.maximum_size=32256
15+
nessuno.upload.maximum_data_size=2048
16+
nessuno.upload.speed=115200
17+
nessuno.bootloader.tool=avrdude
18+
nessuno.bootloader.low_fuses=0xFF
19+
nessuno.bootloader.high_fuses=0xDE
20+
nessuno.bootloader.extended_fuses=0xFD
21+
nessuno.bootloader.unlock_bits=0x3F
22+
nessuno.bootloader.lock_bits=0x0F
23+
nessuno.bootloader.file=optiboot/optiboot_atmega328.hex
24+
nessuno.build.mcu=atmega328p
25+
nessuno.build.f_cpu=16000000L
26+
nessuno.build.board=AVR_NESSUNO
27+
nessuno.build.core=arduino
28+
nessuno.build.variant=standard
29+
30+
nessuno.menu.cache.on=Enabled
31+
nessuno.menu.cache.on.build.cache_flags=-DENABLE_CACHE
32+
nessuno.menu.cache.off=Disabled
33+
nessuno.menu.cache.off.build.cache_flags=
34+
35+
altra.name=Arduino Altra
36+
altra.vid.0=0x2341
37+
altra.pid.0=0x0043
38+
altra.vid.1=0x2341
39+
altra.pid.1=0x0001
40+
altra.vid.2=0x2A03
41+
altra.pid.2=0x0043
42+
altra.vid.3=0x2341
43+
altra.pid.3=0x0243
44+
altra.upload.tool=avrdude
45+
altra.upload.protocol=arduino
46+
altra.upload.maximum_size=32256
47+
altra.upload.maximum_data_size=2048
48+
altra.upload.speed=115200
49+
altra.bootloader.tool=avrdude
50+
altra.bootloader.low_fuses=0xFF
51+
altra.bootloader.high_fuses=0xDE
52+
altra.bootloader.extended_fuses=0xFD
53+
altra.bootloader.unlock_bits=0x3F
54+
altra.bootloader.lock_bits=0x0F
55+
altra.bootloader.file=optiboot/optiboot_atmega328.hex
56+
altra.build.mcu=atmega328p
57+
altra.build.f_cpu=16000000L
58+
altra.build.board=AVR_ALTRA
59+
altra.build.core=arduino
60+
altra.build.variant=standard
61+
altra.menu.cpu.atmega328=ATmega328P
62+
altra.menu.cpu.atmega168=ATmega168
63+
altra.menufoo=bar
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
name=Arduino Test Boards
2+
version=4.2.0

0 commit comments

Comments
 (0)