Skip to content

Commit 0d8ce36

Browse files
cmaglieper1234
andauthored
Pluggable discovery: board identification can now identify also custom board options (#1674)
* Added test for os-specific config options * Build board config options structures only once and cache them * Board's build options properties are now calculated only once and cached * Added tests for config options ordering It required insertion of test data with the properties.Set method to preserve ordering. * Renamed some variables to improve code readability * Added board config identification subroutines * Added board config detection in 'commands.identify' function * Updated docs * Apply suggestions from code review Co-authored-by: per1234 <[email protected]> * Fixed comment Co-authored-by: per1234 <[email protected]>
1 parent 6f14510 commit 0d8ce36

File tree

6 files changed

+565
-279
lines changed

6 files changed

+565
-279
lines changed

Diff for: arduino/cores/board.go

+88-39
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ type Board struct {
2727
BoardID string
2828
Properties *properties.Map `json:"-"`
2929
PlatformRelease *PlatformRelease `json:"-"`
30+
configOptions *properties.Map
31+
configOptionValues map[string]*properties.Map
32+
configOptionProperties map[string]*properties.Map
33+
defaultConfig *properties.Map
3034
identificationProperties []*properties.Map
3135
}
3236

@@ -64,66 +68,74 @@ func (b *Board) String() string {
6468
return b.FQBN()
6569
}
6670

71+
func (b *Board) buildConfigOptionsStructures() {
72+
if b.configOptions != nil {
73+
return
74+
}
75+
76+
b.configOptions = properties.NewMap()
77+
allConfigs := b.Properties.SubTree("menu")
78+
for _, option := range allConfigs.FirstLevelKeys() {
79+
b.configOptions.Set(option, b.PlatformRelease.Menus.Get(option))
80+
}
81+
82+
b.configOptionValues = map[string]*properties.Map{}
83+
b.configOptionProperties = map[string]*properties.Map{}
84+
b.defaultConfig = properties.NewMap()
85+
for option, optionProps := range allConfigs.FirstLevelOf() {
86+
b.configOptionValues[option] = properties.NewMap()
87+
values := optionProps.FirstLevelKeys()
88+
b.defaultConfig.Set(option, values[0])
89+
for _, value := range values {
90+
if label, ok := optionProps.GetOk(value); ok {
91+
b.configOptionValues[option].Set(value, label)
92+
b.configOptionProperties[option+"="+value] = optionProps.SubTree(value)
93+
}
94+
}
95+
}
96+
}
97+
6798
// GetConfigOptions returns an OrderedMap of configuration options for this board.
6899
// The returned map will have key and value as option id and option name, respectively.
69100
func (b *Board) GetConfigOptions() *properties.Map {
70-
res := properties.NewMap()
71-
menu := b.Properties.SubTree("menu")
72-
for _, option := range menu.FirstLevelKeys() {
73-
res.Set(option, b.PlatformRelease.Menus.Get(option))
74-
}
75-
return res
101+
b.buildConfigOptionsStructures()
102+
return b.configOptions
76103
}
77104

78105
// GetConfigOptionValues returns an OrderedMap of possible values for a specific configuratio options
79106
// for this board. The returned map will have key and value as option value and option value name,
80107
// respectively.
81108
func (b *Board) GetConfigOptionValues(option string) *properties.Map {
82-
res := properties.NewMap()
83-
menu := b.Properties.SubTree("menu").SubTree(option)
84-
for _, value := range menu.FirstLevelKeys() {
85-
if label, ok := menu.GetOk(value); ok {
86-
res.Set(value, label)
87-
}
88-
}
89-
return res
109+
b.buildConfigOptionsStructures()
110+
return b.configOptionValues[option]
90111
}
91112

92113
// GetBuildProperties returns the build properties and the build
93114
// platform for the Board with the configuration passed as parameter.
94115
func (b *Board) GetBuildProperties(userConfigs *properties.Map) (*properties.Map, error) {
95-
// Clone user configs because they are destroyed during iteration
96-
userConfigs = userConfigs.Clone()
116+
b.buildConfigOptionsStructures()
117+
118+
// Override default configs with user configs
119+
config := b.defaultConfig.Clone()
120+
config.Merge(userConfigs)
97121

98122
// Start with board's base properties
99123
buildProperties := b.Properties.Clone()
100124

101125
// Add all sub-configurations one by one (a config is: option=value)
102-
menu := b.Properties.SubTree("menu")
103-
for _, option := range menu.FirstLevelKeys() {
104-
optionMenu := menu.SubTree(option)
105-
userValue, haveUserValue := userConfigs.GetOk(option)
106-
if haveUserValue {
107-
userConfigs.Remove(option)
108-
if !optionMenu.ContainsKey(userValue) {
109-
return nil, fmt.Errorf(tr("invalid value '%[1]s' for option '%[2]s'"), userValue, option)
110-
}
111-
} else {
112-
// apply default
113-
userValue = optionMenu.FirstLevelKeys()[0]
114-
}
115-
116-
optionsConf := optionMenu.SubTree(userValue)
117-
buildProperties.Merge(optionsConf)
118-
}
119-
120126
// Check for residual invalid options...
121-
if invalidKeys := userConfigs.Keys(); len(invalidKeys) > 0 {
122-
invalidOption := invalidKeys[0]
123-
if invalidOption == "" {
127+
for option, value := range config.AsMap() {
128+
if option == "" {
124129
return nil, fmt.Errorf(tr("invalid empty option found"))
125130
}
126-
return nil, fmt.Errorf(tr("invalid option '%s'"), invalidOption)
131+
if _, ok := b.configOptions.GetOk(option); !ok {
132+
return nil, fmt.Errorf(tr("invalid option '%s'"), option)
133+
}
134+
optionsConf, ok := b.configOptionProperties[option+"="+value]
135+
if !ok {
136+
return nil, fmt.Errorf(tr("invalid value '%[1]s' for option '%[2]s'"), value, option)
137+
}
138+
buildProperties.Merge(optionsConf)
127139
}
128140

129141
return buildProperties, nil
@@ -153,7 +165,7 @@ func (b *Board) GetIdentificationProperties() []*properties.Map {
153165
}
154166

155167
// IsBoardMatchingIDProperties returns true if the board match the given
156-
// identification properties
168+
// upload port identification properties
157169
func (b *Board) IsBoardMatchingIDProperties(query *properties.Map) bool {
158170
// check checks if the given set of properties p match the "query"
159171
check := func(p *properties.Map) bool {
@@ -179,3 +191,40 @@ func (b *Board) IsBoardMatchingIDProperties(query *properties.Map) bool {
179191
func GetMonitorSettings(protocol string, boardProperties *properties.Map) *properties.Map {
180192
return boardProperties.SubTree("monitor_port." + protocol)
181193
}
194+
195+
// IdentifyBoardConfiguration returns the configuration of the board that can be
196+
// deduced from the given upload port identification properties
197+
func (b *Board) IdentifyBoardConfiguration(query *properties.Map) *properties.Map {
198+
// check checks if the given set of properties p match the "query"
199+
check := func(p *properties.Map) bool {
200+
for k, v := range p.AsMap() {
201+
if !strings.EqualFold(query.Get(k), v) {
202+
return false
203+
}
204+
}
205+
return true
206+
}
207+
checkAll := func(allP []*properties.Map) bool {
208+
for _, p := range allP {
209+
if check(p) {
210+
return true
211+
}
212+
}
213+
return false
214+
}
215+
216+
res := properties.NewMap()
217+
for _, option := range b.GetConfigOptions().Keys() {
218+
values := b.GetConfigOptionValues(option).Keys()
219+
220+
for _, value := range values {
221+
config := option + "=" + value
222+
configProps := b.configOptionProperties[config]
223+
224+
if checkAll(configProps.ExtractSubIndexSets("upload_port")) {
225+
res.Set(option, value)
226+
}
227+
}
228+
}
229+
return res
230+
}

0 commit comments

Comments
 (0)