Skip to content

Commit 1880dae

Browse files
authored
Merge pull request #2215 from geky/tools-cumulative-overrides
[tools] Modified config to aggregate cumulative overrides against targets
2 parents 83f24fb + a5ea143 commit 1880dae

File tree

3 files changed

+68
-39
lines changed

3 files changed

+68
-39
lines changed

tools/config.py

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,41 @@ def __init__(self, name, unit_name, unit_kind):
137137
self.macro_name = name
138138
self.macro_value = None
139139

140+
# Representation of overrides for cumulative attributes
141+
class ConfigCumulativeOverride:
142+
def __init__(self, name, additions=set(), removals=set(), strict=False):
143+
self.name = name
144+
self.additions = set(additions)
145+
self.removals = set(removals)
146+
self.strict = strict
147+
148+
# Add attr to the cumulative override
149+
def remove_cumulative_overrides(self, overrides):
150+
for override in overrides:
151+
if override in self.additions:
152+
raise ConfigException("Configuration conflict. The %s %s both added and removed." % (self.name[:-1], override))
153+
154+
self.removals |= set(overrides)
155+
156+
# Remove attr from the cumulative overrides
157+
def add_cumulative_overrides(self, overrides):
158+
for override in overrides:
159+
if (override in self.removals or (self.strict and override not in self.additions)):
160+
raise ConfigException("Configuration conflict. The %s %s both added and removed." % (self.name[:-1], override))
161+
162+
self.additions |= set(overrides)
163+
164+
# Enable strict set of cumulative overrides for the specified attr
165+
def strict_cumulative_overrides(self, overrides):
166+
self.remove_cumulative_overrides(self.additions - set(overrides))
167+
self.add_cumulative_overrides(overrides)
168+
self.strict = True
169+
170+
def update_target(self, target):
171+
setattr(target, self.name, list(
172+
set(getattr(target, self.name, [])) | self.additions - self.removals))
173+
174+
140175
# 'Config' implements the mbed configuration mechanism
141176
class Config:
142177
# Libraries and applications have different names for their configuration files
@@ -184,9 +219,12 @@ def __init__(self, target, top_level_dirs = []):
184219
self.processed_configs = {}
185220
self.target = target if isinstance(target, basestring) else target.name
186221
self.target_labels = Target.get_target(self.target).get_labels()
187-
self.added_features = set()
188-
self.removed_features = set()
189-
self.removed_unecessary_features = False
222+
223+
self.cumulative_overrides = { key: ConfigCumulativeOverride(key)
224+
for key in Target._Target__cumulative_attributes }
225+
226+
self._process_config_and_overrides(self.app_config_data, {}, "app", "application")
227+
self.target_labels = Target.get_target(self.target).get_labels()
190228

191229
# Add one or more configuration files
192230
def add_config_files(self, flist):
@@ -222,23 +260,6 @@ def _process_config_parameters(self, data, params, unit_name, unit_kind):
222260
params[full_name] = ConfigParameter(name, v if isinstance(v, dict) else {"value": v}, unit_name, unit_kind)
223261
return params
224262

225-
# Add features to the available features
226-
def remove_features(self, features):
227-
for feature in features:
228-
if feature in self.added_features:
229-
raise ConfigException("Configuration conflict. Feature %s both added and removed." % feature)
230-
231-
self.removed_features |= set(features)
232-
233-
# Remove features from the available features
234-
def add_features(self, features):
235-
for feature in features:
236-
if (feature in self.removed_features
237-
or (self.removed_unecessary_features and feature not in self.added_features)):
238-
raise ConfigException("Configuration conflict. Feature %s both added and removed." % feature)
239-
240-
self.added_features |= set(features)
241-
242263
# Helper function: process "config_parameters" and "target_config_overrides" in a given dictionary
243264
# data: the configuration data of the library/appliation
244265
# params: storage for the discovered configuration parameters
@@ -250,21 +271,25 @@ def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
250271
for label, overrides in data.get("target_overrides", {}).items():
251272
# If the label is defined by the target or it has the special value "*", process the overrides
252273
if (label == '*') or (label in self.target_labels):
253-
# Parse out features
254-
if 'target.features' in overrides:
255-
features = overrides['target.features']
256-
self.remove_features(self.added_features - set(features))
257-
self.add_features(features)
258-
self.removed_unecessary_features = True
259-
del overrides['target.features']
260-
261-
if 'target.features_add' in overrides:
262-
self.add_features(overrides['target.features_add'])
263-
del overrides['target.features_add']
264-
265-
if 'target.features_remove' in overrides:
266-
self.remove_features(overrides['target.features_remove'])
267-
del overrides['target.features_remove']
274+
# Check for invalid cumulative overrides in libraries
275+
if (unit_kind == 'library' and
276+
any(attr.startswith('target.extra_labels') for attr in overrides.iterkeys())):
277+
raise ConfigException("Target override '%s' in '%s' is only allowed at the application level"
278+
% ("target.extra_labels", ConfigParameter.get_display_name(unit_name, unit_kind, label)))
279+
280+
# Parse out cumulative overrides
281+
for attr, cumulatives in self.cumulative_overrides.iteritems():
282+
if 'target.'+attr in overrides:
283+
cumulatives.strict_cumulative_overrides(overrides['target.'+attr])
284+
del overrides['target.'+attr]
285+
286+
if 'target.'+attr+'_add' in overrides:
287+
cumulatives.add_cumulative_overrides(overrides['target.'+attr+'_add'])
288+
del overrides['target.'+attr+'_add']
289+
290+
if 'target.'+attr+'_remove' in overrides:
291+
cumulatives.remove_cumulative_overrides(overrides['target.'+attr+'_remove'])
292+
del overrides['target.'+attr+'_remove']
268293

269294
# Consider the others as overrides
270295
for name, v in overrides.items():
@@ -275,6 +300,10 @@ def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
275300
else:
276301
self.config_errors.append(ConfigException("Attempt to override undefined parameter '%s' in '%s'"
277302
% (full_name, ConfigParameter.get_display_name(unit_name, unit_kind, label))))
303+
304+
for cumulatives in self.cumulative_overrides.itervalues():
305+
cumulatives.update_target(Target.get_target(self.target))
306+
278307
return params
279308

280309
# Read and interpret configuration data defined by targets
@@ -389,8 +418,8 @@ def get_config_data_macros(self):
389418
def get_features(self):
390419
params, _ = self.get_config_data()
391420
self._check_required_parameters(params)
392-
features = ((set(Target.get_target(self.target).features)
393-
| self.added_features) - self.removed_features)
421+
self.cumulative_overrides['features'].update_target(Target.get_target(self.target))
422+
features = Target.get_target(self.target).features
394423

395424
for feature in features:
396425
if feature not in self.__allowed_features:

tools/test/config_test/test23/test_data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
expected_results = {
44
"K64F": {
55
"desc": "test feature collisions",
6-
"exception_msg": "Configuration conflict. Feature IPV4 both added and removed."
6+
"exception_msg": "Configuration conflict. The feature IPV4 both added and removed."
77
}
88
}

tools/test/config_test/test25/test_data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
expected_results = {
44
"K64F": {
55
"desc": "test recursive feature collisions",
6-
"exception_msg": "Configuration conflict. Feature UVISOR both added and removed."
6+
"exception_msg": "Configuration conflict. The feature UVISOR both added and removed."
77
}
88
}

0 commit comments

Comments
 (0)