Skip to content

[tools] Modified config to aggregate cumulative overrides against targets #2215

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

Merged
merged 4 commits into from
Jul 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 66 additions & 37 deletions tools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,41 @@ def __init__(self, name, unit_name, unit_kind):
self.macro_name = name
self.macro_value = None

# Representation of overrides for cumulative attributes
class ConfigCumulativeOverride:
def __init__(self, name, additions=set(), removals=set(), strict=False):
self.name = name
self.additions = set(additions)
self.removals = set(removals)
self.strict = strict

# Add attr to the cumulative override
def remove_cumulative_overrides(self, overrides):
for override in overrides:
if override in self.additions:
raise ConfigException("Configuration conflict. The %s %s both added and removed." % (self.name[:-1], override))

self.removals |= set(overrides)

# Remove attr from the cumulative overrides
def add_cumulative_overrides(self, overrides):
for override in overrides:
if (override in self.removals or (self.strict and override not in self.additions)):
raise ConfigException("Configuration conflict. The %s %s both added and removed." % (self.name[:-1], override))

self.additions |= set(overrides)

# Enable strict set of cumulative overrides for the specified attr
def strict_cumulative_overrides(self, overrides):
self.remove_cumulative_overrides(self.additions - set(overrides))
self.add_cumulative_overrides(overrides)
self.strict = True

def update_target(self, target):
setattr(target, self.name, list(
set(getattr(target, self.name, [])) | self.additions - self.removals))


# 'Config' implements the mbed configuration mechanism
class Config:
# Libraries and applications have different names for their configuration files
Expand Down Expand Up @@ -184,9 +219,12 @@ def __init__(self, target, top_level_dirs = []):
self.processed_configs = {}
self.target = target if isinstance(target, basestring) else target.name
self.target_labels = Target.get_target(self.target).get_labels()
self.added_features = set()
self.removed_features = set()
self.removed_unecessary_features = False

self.cumulative_overrides = { key: ConfigCumulativeOverride(key)
for key in Target._Target__cumulative_attributes }

self._process_config_and_overrides(self.app_config_data, {}, "app", "application")
self.target_labels = Target.get_target(self.target).get_labels()

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

# Add features to the available features
def remove_features(self, features):
for feature in features:
if feature in self.added_features:
raise ConfigException("Configuration conflict. Feature %s both added and removed." % feature)

self.removed_features |= set(features)

# Remove features from the available features
def add_features(self, features):
for feature in features:
if (feature in self.removed_features
or (self.removed_unecessary_features and feature not in self.added_features)):
raise ConfigException("Configuration conflict. Feature %s both added and removed." % feature)

self.added_features |= set(features)

# Helper function: process "config_parameters" and "target_config_overrides" in a given dictionary
# data: the configuration data of the library/appliation
# params: storage for the discovered configuration parameters
Expand All @@ -250,21 +271,25 @@ def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
for label, overrides in data.get("target_overrides", {}).items():
# If the label is defined by the target or it has the special value "*", process the overrides
if (label == '*') or (label in self.target_labels):
# Parse out features
if 'target.features' in overrides:
features = overrides['target.features']
self.remove_features(self.added_features - set(features))
self.add_features(features)
self.removed_unecessary_features = True
del overrides['target.features']

if 'target.features_add' in overrides:
self.add_features(overrides['target.features_add'])
del overrides['target.features_add']

if 'target.features_remove' in overrides:
self.remove_features(overrides['target.features_remove'])
del overrides['target.features_remove']
# Check for invalid cumulative overrides in libraries
if (unit_kind == 'library' and
any(attr.startswith('target.extra_labels') for attr in overrides.iterkeys())):
raise ConfigException("Target override '%s' in '%s' is only allowed at the application level"
% ("target.extra_labels", ConfigParameter.get_display_name(unit_name, unit_kind, label)))

# Parse out cumulative overrides
for attr, cumulatives in self.cumulative_overrides.iteritems():
if 'target.'+attr in overrides:
cumulatives.strict_cumulative_overrides(overrides['target.'+attr])
del overrides['target.'+attr]

if 'target.'+attr+'_add' in overrides:
cumulatives.add_cumulative_overrides(overrides['target.'+attr+'_add'])
del overrides['target.'+attr+'_add']

if 'target.'+attr+'_remove' in overrides:
cumulatives.remove_cumulative_overrides(overrides['target.'+attr+'_remove'])
del overrides['target.'+attr+'_remove']

# Consider the others as overrides
for name, v in overrides.items():
Expand All @@ -275,6 +300,10 @@ def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
else:
self.config_errors.append(ConfigException("Attempt to override undefined parameter '%s' in '%s'"
% (full_name, ConfigParameter.get_display_name(unit_name, unit_kind, label))))

for cumulatives in self.cumulative_overrides.itervalues():
cumulatives.update_target(Target.get_target(self.target))

return params

# Read and interpret configuration data defined by targets
Expand Down Expand Up @@ -389,8 +418,8 @@ def get_config_data_macros(self):
def get_features(self):
params, _ = self.get_config_data()
self._check_required_parameters(params)
features = ((set(Target.get_target(self.target).features)
| self.added_features) - self.removed_features)
self.cumulative_overrides['features'].update_target(Target.get_target(self.target))
features = Target.get_target(self.target).features

for feature in features:
if feature not in self.__allowed_features:
Expand Down
2 changes: 1 addition & 1 deletion tools/test/config_test/test23/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
expected_results = {
"K64F": {
"desc": "test feature collisions",
"exception_msg": "Configuration conflict. Feature IPV4 both added and removed."
"exception_msg": "Configuration conflict. The feature IPV4 both added and removed."
}
}
2 changes: 1 addition & 1 deletion tools/test/config_test/test25/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
expected_results = {
"K64F": {
"desc": "test recursive feature collisions",
"exception_msg": "Configuration conflict. Feature UVISOR both added and removed."
"exception_msg": "Configuration conflict. The feature UVISOR both added and removed."
}
}