Skip to content

Implementation of a Board-based flow (WIP) #656

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
41 changes: 30 additions & 11 deletions arduino/cores/fqbn.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,41 @@ func ParseFQBN(fqbnIn string) (*FQBN, error) {
return nil, fmt.Errorf("invalid fqbn: empty board identifier")
}
if len(fqbnParts) > 3 {
for _, pair := range strings.Split(fqbnParts[3], ",") {
parts := strings.SplitN(pair, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid fqbn config: %s", pair)
}
k := strings.TrimSpace(parts[0])
v := strings.TrimSpace(parts[1])
if k == "" {
return nil, fmt.Errorf("invalid fqbn config: %s", pair)
}
fqbn.Configs.Set(k, v)
if err := fqbn.SetConfigs(strings.Split(fqbnParts[3], ",")); err != nil {
return nil, err
}
}
return fqbn, nil
}

// SetConfigs set the configs part of the FQBN with the provided config strings.
// Each config string must be a pair "config=value".
func (fqbn *FQBN) SetConfigs(configs []string) error {
for _, pair := range configs {
parts := strings.SplitN(pair, "=", 2)
if len(parts) != 2 {
return fmt.Errorf("invalid fqbn config: %s", pair)
}
k := strings.TrimSpace(parts[0])
v := strings.TrimSpace(parts[1])
if k == "" {
return fmt.Errorf("invalid fqbn config: %s", pair)
}
fqbn.Configs.Set(k, v)
}
return nil
}

// MustParseFQBN extract an FQBN object from the input string or panics
// if there is an error parsing the FQBN
func MustParseFQBN(fqbnIn string) *FQBN {
res, err := ParseFQBN(fqbnIn)
if err != nil {
panic(err.Error())
}
return res
}

func (fqbn *FQBN) String() string {
res := fqbn.StringWithoutConfig()
if fqbn.Configs.Size() > 0 {
Expand Down
22 changes: 18 additions & 4 deletions arduino/cores/packagemanager/package_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
type PackageManager struct {
Log logrus.FieldLogger
Packages cores.Packages
Registry *BoardsRegistry
IndexDir *paths.Path
PackagesDir *paths.Path
DownloadDir *paths.Path
Expand All @@ -47,9 +48,12 @@ type PackageManager struct {

// NewPackageManager returns a new instance of the PackageManager
func NewPackageManager(indexDir, packagesDir, downloadDir, tempDir *paths.Path) *PackageManager {
registry, _ := LoadBoardRegistry(nil) // TODO ...

return &PackageManager{
Log: logrus.StandardLogger(),
Packages: cores.NewPackages(),
Registry: registry,
IndexDir: indexDir,
PackagesDir: packagesDir,
DownloadDir: downloadDir,
Expand Down Expand Up @@ -112,13 +116,23 @@ func (pm *PackageManager) FindBoardsWithID(id string) []*cores.Board {
return res
}

// FindBoardWithFQBN returns the board identified by the fqbn, or an error
func (pm *PackageManager) FindBoardWithFQBN(fqbnIn string) (*cores.Board, error) {
fqbn, err := cores.ParseFQBN(fqbnIn)
// FindBoard search the board identified by boardArg (board alias or board fqbn).
// It returns FQBN, RegistereBoard and Board or an error if not found.
// The board may be present in registry but not installed, in this case o
func (pm *PackageManager) FindBoard(boardArg string, config []string) (*cores.FQBN, *RegisteredBoard, *cores.Board, error) {
fqbn, registeredBoard, err := pm.Registry.FindBoard(boardArg)
if err != nil {
return nil, fmt.Errorf("parsing fqbn: %s", err)
return fqbn, registeredBoard, nil, err
}
if config != nil {
fqbn.SetConfigs(config)
}
board, err := pm.FindBoardWithFQBN(fqbn)
return fqbn, registeredBoard, board, err
}

// FindBoardWithFQBN returns the board identified by the fqbn, or an error
func (pm *PackageManager) FindBoardWithFQBN(fqbn *cores.FQBN) (*cores.Board, error) {
_, _, board, _, _, err := pm.ResolveFQBN(fqbn)
return board, err
}
Expand Down
12 changes: 6 additions & 6 deletions arduino/cores/packagemanager/package_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ func TestFindBoardWithFQBN(t *testing.T) {
pm := packagemanager.NewPackageManager(customHardware, customHardware, customHardware, customHardware)
pm.LoadHardwareFromDirectory(customHardware)

board, err := pm.FindBoardWithFQBN("arduino:avr:uno")
_, _, board, err := pm.FindBoard("arduino:avr:uno", nil)
require.Nil(t, err)
require.NotNil(t, board)
require.Equal(t, board.Name(), "Arduino/Genuino Uno")

board, err = pm.FindBoardWithFQBN("arduino:avr:mega")
_, _, board, err = pm.FindBoard("arduino:avr:mega", nil)
require.Nil(t, err)
require.NotNil(t, board)
require.Equal(t, board.Name(), "Arduino/Genuino Mega or Mega 2560")
Expand Down Expand Up @@ -178,7 +178,7 @@ func TestBoardOptionsFunctions(t *testing.T) {
pm := packagemanager.NewPackageManager(customHardware, customHardware, customHardware, customHardware)
pm.LoadHardwareFromDirectory(customHardware)

nano, err := pm.FindBoardWithFQBN("arduino:avr:nano")
_, _, nano, err := pm.FindBoard("arduino:avr:nano", nil)
require.Nil(t, err)
require.NotNil(t, nano)
require.Equal(t, nano.Name(), "Arduino Nano")
Expand All @@ -194,7 +194,7 @@ func TestBoardOptionsFunctions(t *testing.T) {
expectedNanoCPUValues.Set("atmega168", "ATmega168")
require.EqualValues(t, expectedNanoCPUValues, nanoCPUValues)

esp8266, err := pm.FindBoardWithFQBN("esp8266:esp8266:generic")
_, _, esp8266, err := pm.FindBoard("esp8266:esp8266:generic", nil)
require.Nil(t, err)
require.NotNil(t, esp8266)
require.Equal(t, esp8266.Name(), "Generic ESP8266 Module")
Expand Down Expand Up @@ -230,7 +230,7 @@ func TestFindToolsRequiredForBoard(t *testing.T) {
loadIndex("http://arduino.esp8266.com/stable/package_esp8266com_index.json")
loadIndex("https://adafruit.github.io/arduino-board-index/package_adafruit_index.json")
require.NoError(t, pm.LoadHardware())
esp32, err := pm.FindBoardWithFQBN("esp32:esp32:esp32")
_, _, esp32, err := pm.FindBoard("esp32:esp32:esp32", nil)
require.NoError(t, err)
esptool231 := pm.FindToolDependency(&cores.ToolDependency{
ToolPackager: "esp32",
Expand Down Expand Up @@ -266,7 +266,7 @@ func TestFindToolsRequiredForBoard(t *testing.T) {
testConflictingToolsInDifferentPackages()
testConflictingToolsInDifferentPackages()

feather, err := pm.FindBoardWithFQBN("adafruit:samd:adafruit_feather_m0_express")
_, _, feather, err := pm.FindBoard("adafruit:samd:adafruit_feather_m0_express", nil)
require.NoError(t, err)
require.NotNil(t, feather)
featherTools, err := pm.FindToolsRequiredForBoard(feather)
Expand Down
107 changes: 107 additions & 0 deletions arduino/cores/packagemanager/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package packagemanager

import (
"net/url"
"strings"

"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/go-paths-helper"
)

// BoardsRegistry is a database with a list of registered boards
type BoardsRegistry struct {
Boards []*RegisteredBoard
fqbnToBoard map[string]*RegisteredBoard
aliastToBoard map[string]*RegisteredBoard
}

// RegisteredBoard contains manifest information for a board
type RegisteredBoard struct {
FQBN *cores.FQBN
Alias string
Name string
ExternalPlatformURL *url.URL
}

// NewBoardRegistry creates a new BoardsRegistry instance
func NewBoardRegistry() *BoardsRegistry {
return &BoardsRegistry{
Boards: []*RegisteredBoard{},
fqbnToBoard: map[string]*RegisteredBoard{},
aliastToBoard: map[string]*RegisteredBoard{},
}
}

func (r *BoardsRegistry) addBoard(board *RegisteredBoard) {
r.Boards = append(r.Boards, board)
r.fqbnToBoard[board.FQBN.String()] = board
r.aliastToBoard[board.Alias] = board
}

// FindBoard gets a RegisteredBoard using FQBN or Board Alias
func (r *BoardsRegistry) FindBoard(fqbnOrAlias string) (*cores.FQBN, *RegisteredBoard, error) {
if found, ok := r.aliastToBoard[fqbnOrAlias]; ok {
return found.FQBN, found, nil
}
fqbn, err := cores.ParseFQBN(fqbnOrAlias)
if err != nil {
return nil, nil, err
}
if found, ok := r.fqbnToBoard[fqbn.StringWithoutConfig()]; ok {
return fqbn, found, nil
}
return fqbn, nil, nil
}

// SearchBoards search for a RegisteredBoard using a query string
func (r *BoardsRegistry) SearchBoards(query string) []*RegisteredBoard {
found := []*RegisteredBoard{}
contains := func(a string, b string) bool {
return strings.Contains(strings.ToLower(a), strings.ToLower(b))
}
for _, board := range r.Boards {
if contains(board.Name, query) || contains(board.Alias, query) {
found = append(found, board)
}
}
return found
}

// LoadBoardRegistry retrieve a board registry from a file. WIP...
func LoadBoardRegistry(file *paths.Path) (*BoardsRegistry, error) {

// TODO...

fake := NewBoardRegistry()
fake.addBoard(&RegisteredBoard{
Name: "Arduino Uno",
FQBN: cores.MustParseFQBN("arduino:avr:uno"),
Alias: "uno",
})
fake.addBoard(&RegisteredBoard{
Name: "Arduino Nano",
FQBN: cores.MustParseFQBN("arduino:avr:nano"),
Alias: "nano",
})
fake.addBoard(&RegisteredBoard{
Name: "Arduino Zero",
FQBN: cores.MustParseFQBN("arduino:samd:arduino_zero_edbg"),
Alias: "zero",
})
return fake, nil
}
4 changes: 3 additions & 1 deletion cli/board/board.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ func NewCommand() *cobra.Command {
boardCommand.AddCommand(initDetailsCommand())
boardCommand.AddCommand(initListCommand())
boardCommand.AddCommand(listAllCommand)

boardCommand.AddCommand(initInstallCommand())
boardCommand.AddCommand(initUninstallCommand())
boardCommand.AddCommand(initSearchCommand())
return boardCommand
}
24 changes: 15 additions & 9 deletions cli/board/details.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package board
import (
"context"
"fmt"
"os"

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/feedback"
"github.com/arduino/arduino-cli/cli/instance"
Expand All @@ -26,26 +28,29 @@ import (
"github.com/arduino/arduino-cli/table"
"github.com/fatih/color"
"github.com/spf13/cobra"
"os"
)

var showFullDetails bool

func initDetailsCommand() *cobra.Command {
var detailsCommand = &cobra.Command{
Use: "details <FQBN>",
Use: "details <FQBN | BOARD_ALIAS>",
Short: "Print details about a board.",
Long: "Show information about a board, in particular if the board has options to be specified in the FQBN.",
Example: " " + os.Args[0] + " board details arduino:avr:nano",
Args: cobra.ExactArgs(1),
Run: runDetailsCommand,
}

detailsCommand.Flags().BoolVarP(&showFullDetails, "full", "f", false, "Include full details in text output")

detailsCommand.Flags().BoolVarP(&detailsFlags.showFullDetails, "full", "f", false, "Include full details in text output")
detailsFlags.boardConfig =
detailsCommand.Flags().StringSliceP("board-conf", "c", nil, "set a board configuration value. The flag can be used multiple times.\n"+"Example: "+os.Args[0]+" board details arduino:avr:nano -c cpu=atmega168")
return detailsCommand
}

var detailsFlags struct {
showFullDetails bool
boardConfig *[]string
}

func runDetailsCommand(cmd *cobra.Command, args []string) {
inst, err := instance.CreateInstance()
if err != nil {
Expand All @@ -54,8 +59,9 @@ func runDetailsCommand(cmd *cobra.Command, args []string) {
}

res, err := board.Details(context.Background(), &rpc.BoardDetailsReq{
Instance: inst,
Fqbn: args[0],
Instance: inst,
Board: args[0],
BoardConfig: *detailsFlags.boardConfig,
})

if err != nil {
Expand Down Expand Up @@ -131,7 +137,7 @@ func (dr detailsResult) String() string {
t.AddRow() // get some space from above
for _, tool := range details.ToolsDependencies {
t.AddRow("Required tools:", tool.Packager+":"+tool.Name, "", tool.Version)
if showFullDetails {
if detailsFlags.showFullDetails {
for _, sys := range tool.Systems {
t.AddRow("", "OS:", "", sys.Host)
t.AddRow("", "File:", "", sys.ArchiveFileName)
Expand Down
Loading