diff --git a/libcxx/test/libcxx/selftest/dsl/dsl.sh.py b/libcxx/test/libcxx/selftest/dsl/dsl.sh.py --- a/libcxx/test/libcxx/selftest/dsl/dsl.sh.py +++ b/libcxx/test/libcxx/selftest/dsl/dsl.sh.py @@ -275,127 +275,100 @@ def test_trivial(self): feature = dsl.Feature(name='name') origSubstitutions = copy.deepcopy(self.config.substitutions) - self.assertTrue(feature.isSupported(self.config)) - feature.enableIn(self.config) + actions = feature.getActions(self.config) + self.assertTrue(len(actions) == 1) + for a in actions: + a.applyTo(self.config) self.assertEqual(origSubstitutions, self.config.substitutions) self.assertIn('name', self.config.available_features) def test_name_can_be_a_callable(self): feature = dsl.Feature(name=lambda cfg: 'name') - assert feature.isSupported(self.config) - self.assertEqual('name', feature.getName(self.config)) - feature.enableIn(self.config) + for a in feature.getActions(self.config): + a.applyTo(self.config) self.assertIn('name', self.config.available_features) def test_name_is_not_a_string_1(self): feature = dsl.Feature(name=None) - assert feature.isSupported(self.config) - self.assertRaises(ValueError, lambda: feature.getName(self.config)) - self.assertRaises(ValueError, lambda: feature.enableIn(self.config)) + self.assertRaises(ValueError, lambda: feature.getActions(self.config)) + self.assertRaises(ValueError, lambda: feature.pretty(self.config)) def test_name_is_not_a_string_2(self): feature = dsl.Feature(name=lambda cfg: None) - assert feature.isSupported(self.config) - self.assertRaises(ValueError, lambda: feature.getName(self.config)) - self.assertRaises(ValueError, lambda: feature.enableIn(self.config)) + self.assertRaises(ValueError, lambda: feature.getActions(self.config)) + self.assertRaises(ValueError, lambda: feature.pretty(self.config)) - def test_getName_when_unsupported(self): - feature = dsl.Feature(name='name', when=lambda _: False) - assert not feature.isSupported(self.config) - self.assertRaises(AssertionError, lambda: feature.getName(self.config)) - - def test_adding_compile_flag(self): - feature = dsl.Feature(name='name', compileFlag='-foo') + def test_adding_action(self): + feature = dsl.Feature(name='name', actions=[dsl.AddCompileFlag('-std=c++03')]) origLinkFlags = copy.deepcopy(self.getSubstitution('%{link_flags}')) - assert feature.isSupported(self.config) - feature.enableIn(self.config) + for a in feature.getActions(self.config): + a.applyTo(self.config) self.assertIn('name', self.config.available_features) - self.assertIn('-foo', self.getSubstitution('%{compile_flags}')) + self.assertIn('-std=c++03', self.getSubstitution('%{compile_flags}')) self.assertEqual(origLinkFlags, self.getSubstitution('%{link_flags}')) - def test_compile_flag_can_be_a_callable(self): + def test_actions_can_be_a_callable(self): feature = dsl.Feature(name='name', - compileFlag=lambda cfg: (self.assertIs(self.config, cfg), '-foo')[1]) - assert feature.isSupported(self.config) - feature.enableIn(self.config) - self.assertIn('-foo', self.getSubstitution('%{compile_flags}')) - - def test_adding_link_flag(self): - feature = dsl.Feature(name='name', linkFlag='-foo') - origCompileFlags = copy.deepcopy(self.getSubstitution('%{compile_flags}')) - assert feature.isSupported(self.config) - feature.enableIn(self.config) - self.assertIn('name', self.config.available_features) - self.assertIn('-foo', self.getSubstitution('%{link_flags}')) - self.assertEqual(origCompileFlags, self.getSubstitution('%{compile_flags}')) - - def test_link_flag_can_be_a_callable(self): - feature = dsl.Feature(name='name', - linkFlag=lambda cfg: (self.assertIs(self.config, cfg), '-foo')[1]) - assert feature.isSupported(self.config) - feature.enableIn(self.config) - self.assertIn('-foo', self.getSubstitution('%{link_flags}')) - - def test_adding_both_flags(self): - feature = dsl.Feature(name='name', compileFlag='-hello', linkFlag='-world') - assert feature.isSupported(self.config) - feature.enableIn(self.config) - self.assertIn('name', self.config.available_features) - - self.assertIn('-hello', self.getSubstitution('%{compile_flags}')) - self.assertNotIn('-world', self.getSubstitution('%{compile_flags}')) - - self.assertIn('-world', self.getSubstitution('%{link_flags}')) - self.assertNotIn('-hello', self.getSubstitution('%{link_flags}')) + actions=lambda cfg: ( + self.assertIs(self.config, cfg), + [dsl.AddCompileFlag('-std=c++03')] + )[1]) + for a in feature.getActions(self.config): + a.applyTo(self.config) + self.assertIn('-std=c++03', self.getSubstitution('%{compile_flags}')) def test_unsupported_feature(self): feature = dsl.Feature(name='name', when=lambda _: False) - self.assertFalse(feature.isSupported(self.config)) - # Also make sure we assert if we ever try to add it to a config - self.assertRaises(AssertionError, lambda: feature.enableIn(self.config)) + self.assertEqual(feature.getActions(self.config), []) def test_is_supported_gets_passed_the_config(self): feature = dsl.Feature(name='name', when=lambda cfg: (self.assertIs(self.config, cfg), True)[1]) - self.assertTrue(feature.isSupported(self.config)) + self.assertEqual(len(feature.getActions(self.config)), 1) + +def _throw(): + raise ValueError() class TestParameter(SetupConfigs): """ Tests for libcxx.test.dsl.Parameter """ def test_empty_name_should_blow_up(self): - self.assertRaises(ValueError, lambda: dsl.Parameter(name='', choices=['c++03'], type=str, help='', feature=lambda _: None)) + self.assertRaises(ValueError, lambda: dsl.Parameter(name='', choices=['c++03'], type=str, help='', actions=lambda _: [])) def test_empty_choices_should_blow_up(self): - self.assertRaises(ValueError, lambda: dsl.Parameter(name='std', choices=[], type=str, help='', feature=lambda _: None)) + self.assertRaises(ValueError, lambda: dsl.Parameter(name='std', choices=[], type=str, help='', actions=lambda _: [])) def test_name_is_set_correctly(self): - param = dsl.Parameter(name='std', choices=['c++03'], type=str, help='', feature=lambda _: None) + param = dsl.Parameter(name='std', choices=['c++03'], type=str, help='', actions=lambda _: []) self.assertEqual(param.name, 'std') def test_no_value_provided_and_no_default_value(self): - param = dsl.Parameter(name='std', choices=['c++03'], type=str, help='', feature=lambda _: None) - self.assertRaises(ValueError, lambda: param.getFeature(self.config, self.litConfig.params)) + param = dsl.Parameter(name='std', choices=['c++03'], type=str, help='', actions=lambda _: []) + self.assertRaises(ValueError, lambda: param.getActions(self.config, self.litConfig.params)) def test_no_value_provided_and_default_value(self): param = dsl.Parameter(name='std', choices=['c++03'], type=str, help='', default='c++03', - feature=lambda std: dsl.Feature(name=std)) - param.getFeature(self.config, self.litConfig.params).enableIn(self.config) + actions=lambda std: [dsl.AddFeature(std)]) + for a in param.getActions(self.config, self.litConfig.params): + a.applyTo(self.config) self.assertIn('c++03', self.config.available_features) def test_value_provided_on_command_line_and_no_default_value(self): self.litConfig.params['std'] = 'c++03' param = dsl.Parameter(name='std', choices=['c++03'], type=str, help='', - feature=lambda std: dsl.Feature(name=std)) - param.getFeature(self.config, self.litConfig.params).enableIn(self.config) + actions=lambda std: [dsl.AddFeature(std)]) + for a in param.getActions(self.config, self.litConfig.params): + a.applyTo(self.config) self.assertIn('c++03', self.config.available_features) def test_value_provided_on_command_line_and_default_value(self): """The value provided on the command line should override the default value""" self.litConfig.params['std'] = 'c++11' param = dsl.Parameter(name='std', choices=['c++03', 'c++11'], type=str, default='c++03', help='', - feature=lambda std: dsl.Feature(name=std)) - param.getFeature(self.config, self.litConfig.params).enableIn(self.config) + actions=lambda std: [dsl.AddFeature(std)]) + for a in param.getActions(self.config, self.litConfig.params): + a.applyTo(self.config) self.assertIn('c++11', self.config.available_features) self.assertNotIn('c++03', self.config.available_features) @@ -403,8 +376,9 @@ """The value provided in the config should override the default value""" self.config.std ='c++11' param = dsl.Parameter(name='std', choices=['c++03', 'c++11'], type=str, default='c++03', help='', - feature=lambda std: dsl.Feature(name=std)) - param.getFeature(self.config, self.litConfig.params).enableIn(self.config) + actions=lambda std: [dsl.AddFeature(std)]) + for a in param.getActions(self.config, self.litConfig.params): + a.applyTo(self.config) self.assertIn('c++11', self.config.available_features) self.assertNotIn('c++03', self.config.available_features) @@ -413,42 +387,45 @@ self.config.std = 'c++11' self.litConfig.params['std'] = 'c++03' param = dsl.Parameter(name='std', choices=['c++03', 'c++11'], type=str, help='', - feature=lambda std: dsl.Feature(name=std)) - param.getFeature(self.config, self.litConfig.params).enableIn(self.config) + actions=lambda std: [dsl.AddFeature(std)]) + for a in param.getActions(self.config, self.litConfig.params): + a.applyTo(self.config) self.assertIn('c++03', self.config.available_features) self.assertNotIn('c++11', self.config.available_features) - def test_feature_is_None(self): + def test_no_actions(self): self.litConfig.params['std'] = 'c++03' param = dsl.Parameter(name='std', choices=['c++03'], type=str, help='', - feature=lambda _: None) - feature = param.getFeature(self.config, self.litConfig.params) - self.assertIsNone(feature) + actions=lambda _: []) + actions = param.getActions(self.config, self.litConfig.params) + self.assertEqual(actions, []) def test_boolean_value_parsed_from_trueish_string_parameter(self): self.litConfig.params['enable_exceptions'] = "True" param = dsl.Parameter(name='enable_exceptions', choices=[True, False], type=bool, help='', - feature=lambda exceptions: None if exceptions else ValueError()) - self.assertIsNone(param.getFeature(self.config, self.litConfig.params)) + actions=lambda exceptions: [] if exceptions else _throw()) + self.assertEqual(param.getActions(self.config, self.litConfig.params), []) def test_boolean_value_from_true_boolean_parameter(self): self.litConfig.params['enable_exceptions'] = True param = dsl.Parameter(name='enable_exceptions', choices=[True, False], type=bool, help='', - feature=lambda exceptions: None if exceptions else ValueError()) - self.assertIsNone(param.getFeature(self.config, self.litConfig.params)) + actions=lambda exceptions: [] if exceptions else _throw()) + self.assertEqual(param.getActions(self.config, self.litConfig.params), []) def test_boolean_value_parsed_from_falseish_string_parameter(self): self.litConfig.params['enable_exceptions'] = "False" param = dsl.Parameter(name='enable_exceptions', choices=[True, False], type=bool, help='', - feature=lambda exceptions: None if exceptions else dsl.Feature(name="-fno-exceptions")) - param.getFeature(self.config, self.litConfig.params).enableIn(self.config) + actions=lambda exceptions: [] if exceptions else [dsl.AddFeature("-fno-exceptions")]) + for a in param.getActions(self.config, self.litConfig.params): + a.applyTo(self.config) self.assertIn('-fno-exceptions', self.config.available_features) def test_boolean_value_from_false_boolean_parameter(self): self.litConfig.params['enable_exceptions'] = False param = dsl.Parameter(name='enable_exceptions', choices=[True, False], type=bool, help='', - feature=lambda exceptions: None if exceptions else dsl.Feature(name="-fno-exceptions")) - param.getFeature(self.config, self.litConfig.params).enableIn(self.config) + actions=lambda exceptions: [] if exceptions else [dsl.AddFeature("-fno-exceptions")]) + for a in param.getActions(self.config, self.litConfig.params): + a.applyTo(self.config) self.assertIn('-fno-exceptions', self.config.available_features) diff --git a/libcxx/utils/libcxx/test/dsl.py b/libcxx/utils/libcxx/test/dsl.py --- a/libcxx/utils/libcxx/test/dsl.py +++ b/libcxx/utils/libcxx/test/dsl.py @@ -217,6 +217,124 @@ return {m: int(v.rstrip('LlUu')) for (m, v) in allMacros.items() if m.startswith('__cpp_')} +def _addToSubstitution(substitutions, key, value): + return [(k, v + ' ' + value) if k == key else (k, v) for (k, v) in substitutions] + + +class ConfigAction(object): + """ + This class represents an action that can be performed on a Lit TestingConfig + object. + + Examples of such actions are adding or modifying substitutions, Lit features, + etc. This class only provides the interface of such actions, and it is meant + to be subclassed appropriately to create new actions. + """ + def applyTo(self, config): + """ + Applies the action to the given configuration. + + This should modify the configuration object in place, and return nothing. + + If applying the action to the configuration would yield an invalid + configuration, and it is possible to diagnose it here, this method + should produce an error. For example, it should be an error to modify + a substitution in a way that we know for sure is invalid (e.g. adding + a compiler flag when we know the compiler doesn't support it). Failure + to do so early may lead to difficult-to-diagnose issues down the road. + """ + pass + + def pretty(self, config, litParams): + """ + Returns a short and human-readable string describing what this action does. + + This is used for logging purposes when running the test suite, so it should + be kept concise. + """ + pass + + +class AddFeature(ConfigAction): + """ + This action defines the given Lit feature when running the test suite. + + The name of the feature can be a string or a callable, in which case it is + called with the configuration to produce the feature name (as a string). + """ + def __init__(self, name): + self._name = name + + def _getName(self, config): + name = self._name(config) if callable(self._name) else self._name + if not isinstance(name, str): + raise ValueError("Lit feature did not resolve to a string (got {})".format(name)) + return name + + def applyTo(self, config): + config.available_features.add(self._getName(config)) + + def pretty(self, config, litParams): + return 'add Lit feature {}'.format(self._getName(config)) + + +class AddFlag(ConfigAction): + """ + This action adds the given flag to the %{flags} substitution. + + The flag can be a string or a callable, in which case it is called with the + configuration to produce the actual flag (as a string). + """ + def __init__(self, flag): + self._getFlag = lambda config: flag(config) if callable(flag) else flag + + def applyTo(self, config): + flag = self._getFlag(config) + assert hasCompileFlag(config, flag), "Trying to enable flag {}, which is not supported".format(flag) + config.substitutions = _addToSubstitution(config.substitutions, '%{flags}', flag) + + def pretty(self, config, litParams): + return 'add {} to %{{flags}}'.format(self._getFlag(config)) + + +class AddCompileFlag(ConfigAction): + """ + This action adds the given flag to the %{compile_flags} substitution. + + The flag can be a string or a callable, in which case it is called with the + configuration to produce the actual flag (as a string). + """ + def __init__(self, flag): + self._getFlag = lambda config: flag(config) if callable(flag) else flag + + def applyTo(self, config): + flag = self._getFlag(config) + assert hasCompileFlag(config, flag), "Trying to enable compile flag {}, which is not supported".format(flag) + config.substitutions = _addToSubstitution(config.substitutions, '%{compile_flags}', flag) + + def pretty(self, config, litParams): + return 'add {} to %{{compile_flags}}'.format(self._getFlag(config)) + + +class AddLinkFlag(ConfigAction): + """ + This action adds the given flag to the %{link_flags} substitution. + + The flag can be a string or a callable, in which case it is called with the + configuration to produce the actual flag (as a string). + """ + def __init__(self, flag): + self._getFlag = lambda config: flag(config) if callable(flag) else flag + + def applyTo(self, config): + flag = self._getFlag(config) + assert hasCompileFlag(config, flag), "Trying to enable link flag {}, which is not supported".format(flag) + config.substitutions = _addToSubstitution(config.substitutions, '%{link_flags}', flag) + + def pretty(self, config, litParams): + return 'add {} to %{{link_flags}}'.format(self._getFlag(config)) + + class Feature(object): """ Represents a Lit available feature that is enabled whenever it is supported. @@ -226,7 +344,7 @@ control whether a Feature is enabled -- it should be enabled whenever it is supported. """ - def __init__(self, name, compileFlag=None, linkFlag=None, when=lambda _: True): + def __init__(self, name, actions=None, when=lambda _: True): """ Create a Lit feature for consumption by a test suite. @@ -236,17 +354,13 @@ callable, in which case it is passed the TestingConfig and should generate a string representing the name of the feature. - - compileFlag - An optional compile flag to add when this feature is added to a - TestingConfig. If provided, this must be a string representing a - compile flag that will be appended to the end of the %{compile_flags} - substitution of the TestingConfig. - - - linkFlag - An optional link flag to add when this feature is added to a - TestingConfig. If provided, this must be a string representing a - link flag that will be appended to the end of the %{link_flags} - substitution of the TestingConfig. + - actions + An optional list of ConfigActions to apply when the feature is supported. + An AddFeature action is always created regardless of any actions supplied + here -- these actions are meant to perform more than setting a corresponding + Lit feature (e.g. adding compiler flags). If 'actions' is a callable, it + is called with the current configuration object to generate the actual + list of actions. - when A callable that gets passed a TestingConfig and should return a @@ -257,52 +371,35 @@ supported. """ self._name = name - self._compileFlag = compileFlag - self._linkFlag = linkFlag + self._actions = [] if actions is None else actions self._isSupported = when - def isSupported(self, config): - """ - Return whether the feature is supported by the given TestingConfig. - """ - return self._isSupported(config) - - def getName(self, config): - """ - Return the name of the feature. - - It is an error to call `f.getName(cfg)` if the feature `f` is not supported. - """ - assert self.isSupported(config), \ - "Trying to get the name of a feature that is not supported in the given configuration" + def _getName(self, config): name = self._name(config) if callable(self._name) else self._name if not isinstance(name, str): raise ValueError("Feature did not resolve to a name that's a string, got {}".format(name)) return name - def enableIn(self, config): + def getActions(self, config): """ - Enable a feature in a TestingConfig. + Return the list of actions associated to this feature. - The name of the feature is added to the set of available features of - `config`, and any compile or link flags provided upon construction of - the Feature are added to the end of the corresponding substitution in - the config. - - It is an error to call `f.enableIn(cfg)` if the feature `f` is not - supported in that TestingConfig (i.e. if `not f.isSupported(cfg)`). + If the feature is not supported, an empty list is returned. + If the feature is supported, an `AddFeature` action is automatically added + to the returned list of actions, in addition to any actions provided on + construction. """ - assert self.isSupported(config), \ - "Trying to enable feature {} that is not supported in the given configuration".format(self._name) + if not self._isSupported(config): + return [] + else: + actions = self._actions(config) if callable(self._actions) else self._actions + return [AddFeature(self._getName(config))] + actions - addTo = lambda subs, sub, flag: [(s, x + ' ' + flag) if s == sub else (s, x) for (s, x) in subs] - if self._compileFlag: - compileFlag = self._compileFlag(config) if callable(self._compileFlag) else self._compileFlag - config.substitutions = addTo(config.substitutions, '%{compile_flags}', compileFlag) - if self._linkFlag: - linkFlag = self._linkFlag(config) if callable(self._linkFlag) else self._linkFlag - config.substitutions = addTo(config.substitutions, '%{link_flags}', linkFlag) - config.available_features.add(self.getName(config)) + def pretty(self, config): + """ + Returns the Feature's name. + """ + return self._getName(config) def _str_to_bool(s): @@ -339,11 +436,13 @@ that use that `config` object. Parameters can have multiple possible values, and they can have a default - value when left unspecified. They can also have a Feature associated to them, - in which case the Feature is added to the TestingConfig if the parameter is - enabled. It is an error if the Parameter is enabled but the Feature associated - to it is not supported, for example trying to set the compilation standard to - C++17 when `-std=c++17` is not supported by the compiler. + value when left unspecified. They can also have any number of ConfigActions + associated to them, in which case the actions will be performed on the + TestingConfig if the parameter is enabled. Depending on the actions + associated to a Parameter, it may be an error to enable the Parameter + if some actions are not supported in the given configuration. For example, + trying to set the compilation standard to C++23 when `-std=c++23` is not + supported by the compiler would be an error. One important point is that Parameters customize the behavior of the test suite in a bounded way, i.e. there should be a finite set of possible choices @@ -356,7 +455,7 @@ compiler), it can be handled in the `lit.cfg`, but it shouldn't be represented with a Parameter. """ - def __init__(self, name, choices, type, help, feature, default=None): + def __init__(self, name, choices, type, help, actions, default=None): """ Create a Lit parameter to customize the behavior of a test suite. @@ -380,10 +479,11 @@ A string explaining the parameter, for documentation purposes. TODO: We should be able to surface those from the Lit command-line. - - feature + - actions A callable that gets passed the parsed value of the parameter (either the one passed on the command-line or the default one), and that returns - either None or a Feature. + a list of ConfigAction to perform given the value of the parameter. + All the ConfigAction must be supported in the given configuration. - default An optional default value to use for the parameter when no value is @@ -403,20 +503,13 @@ self._parse = lambda x: (_str_to_bool(x) if type is bool and isinstance(x, str) else type(x)) self._help = help - self._feature = feature + self._actions = actions self._default = default - @property - def name(self): + def _getValue(self, config, litParams): """ - Return the name of the parameter. - - This is the name that can be used to set the parameter on the command-line - when running Lit. + Return the value of the parameter given the configuration objects. """ - return self._name - - def getFeature(self, config, litParams): param = getattr(config, self.name, None) param = litParams.get(self.name, param) if param is None and self._default is None: @@ -425,4 +518,26 @@ value = self._parse(param) if param is not None else getDefault() if value not in self._choices: raise ValueError("Got value '{}' for parameter '{}', which is not in the provided set of possible choices: {}".format(value, self.name, self._choices)) - return self._feature(value) + return value + + @property + def name(self): + """ + Return the name of the parameter. + + This is the name that can be used to set the parameter on the command-line + when running Lit. + """ + return self._name + + def getActions(self, config, litParams): + """ + Return the list of actions associated to this value of the parameter. + """ + return self._actions(self._getValue(config, litParams)) + + def pretty(self, config, litParams): + """ + Return a pretty representation of the parameter's name and value. + """ + return "{}={}".format(self.name, self._getValue(config, litParams)) diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py --- a/libcxx/utils/libcxx/test/features.py +++ b/libcxx/utils/libcxx/test/features.py @@ -14,11 +14,19 @@ _isGCC = lambda cfg: '__GNUC__' in compilerMacros(cfg) and '__clang__' not in compilerMacros(cfg) DEFAULT_FEATURES = [ - Feature(name='fcoroutines-ts', compileFlag='-fcoroutines-ts', + Feature(name='fcoroutines-ts', when=lambda cfg: hasCompileFlag(cfg, '-fcoroutines-ts') and - featureTestMacros(cfg, flags='-fcoroutines-ts').get('__cpp_coroutines', 0) >= 201703), + featureTestMacros(cfg, flags='-fcoroutines-ts').get('__cpp_coroutines', 0) >= 201703, + actions=[AddCompileFlag('-fcoroutines-ts')]), + + Feature(name='thread-safety', + when=lambda cfg: hasCompileFlag(cfg, '-Werror=thread-safety'), + actions=[AddCompileFlag('-Werror=thread-safety')]), + + Feature(name='diagnose-if-support', + when=lambda cfg: hasCompileFlag(cfg, '-Wuser-defined-warnings'), + actions=[AddCompileFlag('-Wuser-defined-warnings')]), - Feature(name='thread-safety', when=lambda cfg: hasCompileFlag(cfg, '-Werror=thread-safety'), compileFlag='-Werror=thread-safety'), Feature(name='has-fblocks', when=lambda cfg: hasCompileFlag(cfg, '-fblocks')), Feature(name='-fsized-deallocation', when=lambda cfg: hasCompileFlag(cfg, '-fsized-deallocation')), Feature(name='-faligned-allocation', when=lambda cfg: hasCompileFlag(cfg, '-faligned-allocation')), @@ -30,7 +38,6 @@ Feature(name='has-fobjc-arc', when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc') and sys.platform.lower().strip() == 'darwin'), # TODO: this doesn't handle cross-compiling to Apple platforms. Feature(name='objective-c++', when=lambda cfg: hasCompileFlag(cfg, '-xobjective-c++ -fobjc-arc')), - Feature(name='diagnose-if-support', when=lambda cfg: hasCompileFlag(cfg, '-Wuser-defined-warnings'), compileFlag='-Wuser-defined-warnings'), Feature(name='modules-support', when=lambda cfg: hasCompileFlag(cfg, '-fmodules')), Feature(name='non-lockfree-atomics', when=lambda cfg: sourceBuilds(cfg, """ #include @@ -87,9 +94,11 @@ # FIXME: This is a hack that should be fixed using module maps. # If modules are enabled then we have to lift all of the definitions # in <__config_site> onto the command line. - compileFlag=lambda cfg, m=macro: '-Wno-macro-redefined -D{}'.format(m) + ( - '={}'.format(compilerMacros(cfg)[m]) if compilerMacros(cfg)[m] else '' - ) + actions=lambda cfg, m=macro: [ + AddCompileFlag('-Wno-macro-redefined -D{}'.format(m) + ( + '={}'.format(compilerMacros(cfg)[m]) if compilerMacros(cfg)[m] else '' + )) + ] ) ] diff --git a/libcxx/utils/libcxx/test/newconfig.py b/libcxx/utils/libcxx/test/newconfig.py --- a/libcxx/utils/libcxx/test/newconfig.py +++ b/libcxx/utils/libcxx/test/newconfig.py @@ -13,23 +13,25 @@ raise ValueError('Substitution {} is not in the config.'.format(substitution)) def configure(parameters, features, config, lit_config): - # Apply parameters to the configuration first, since parameters are things - # that we request explicitly and which might influence what features are - # implicitly made available next. + # Apply the actions supplied by parameters to the configuration first, since + # parameters are things that we request explicitly and which might influence + # what features are implicitly made available next. for param in parameters: - feature = param.getFeature(config, lit_config.params) - if feature: - feature.enableIn(config) - lit_config.note("Enabling Lit feature '{}' as a result of parameter '{}'".format(feature.getName(config), param.name)) + actions = param.getActions(config, lit_config.params) + for action in actions: + action.applyTo(config) + lit_config.note("Applied '{}' as a result of parameter '{}'".format( + action.pretty(config, lit_config.params), + param.pretty(config, lit_config.params))) # Then, apply the automatically-detected features. - printFeatures = [] for feature in features: - if feature.isSupported(config): - feature.enableIn(config) - printFeatures.append(feature.getName(config)) - printFeatures = ["'{}'".format(f) for f in sorted(printFeatures)] - lit_config.note("Enabling implicitly detected Lit features {}".format(', '.join(printFeatures))) + actions = feature.getActions(config) + for action in actions: + action.applyTo(config) + lit_config.note("Applied '{}' as a result of implicitly detected feature '{}'".format( + action.pretty(config, lit_config.params), + feature.pretty(config))) # Print the basic substitutions for sub in ('%{cxx}', '%{flags}', '%{compile_flags}', '%{link_flags}', '%{exec}'): diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -15,40 +15,54 @@ Parameter(name='std', choices=_allStandards, type=str, help="The version of the standard to compile the test suite with.", default=lambda cfg: next(s for s in reversed(_allStandards) if hasCompileFlag(cfg, '-std='+s)), - feature=lambda std: - Feature(name=std, compileFlag='-std={}'.format(std), - when=lambda cfg: hasCompileFlag(cfg, '-std={}'.format(std)))), + actions=lambda std: [ + AddFeature(std), + AddCompileFlag('-std={}'.format(std)), + ]), Parameter(name='enable_exceptions', choices=[True, False], type=bool, default=True, help="Whether to enable exceptions when compiling the test suite.", - feature=lambda exceptions: None if exceptions else - Feature(name='no-exceptions', compileFlag='-fno-exceptions')), + actions=lambda exceptions: [] if exceptions else [ + AddFeature('no-exceptions'), + AddCompileFlag('-fno-exceptions') + ]), Parameter(name='enable_rtti', choices=[True, False], type=bool, default=True, help="Whether to enable RTTI when compiling the test suite.", - feature=lambda rtti: None if rtti else - Feature(name='no-rtti', compileFlag='-fno-rtti')), + actions=lambda rtti: [] if rtti else [ + AddFeature('no-rtti'), + AddCompileFlag('-fno-rtti') + ]), Parameter(name='stdlib', choices=['libc++', 'libstdc++', 'msvc'], type=str, default='libc++', help="The C++ Standard Library implementation being tested.", - feature=lambda stdlib: Feature(name=stdlib)), + actions=lambda stdlib: [ + AddFeature(stdlib) + ]), # Parameters to enable or disable parts of the test suite Parameter(name='enable_filesystem', choices=[True, False], type=bool, default=True, help="Whether to enable tests for the C++ library.", - feature=lambda filesystem: None if filesystem else - Feature(name='c++filesystem-disabled')), + actions=lambda filesystem: [] if filesystem else [ + AddFeature('c++filesystem-disabled') + ]), Parameter(name='enable_experimental', choices=[True, False], type=bool, default=False, - help="Whether to enable tests for experimental C++ libraries (typically Library Fundamentals TSes).", - feature=lambda experimental: None if not experimental else - Feature(name='c++experimental', linkFlag='-lc++experimental')), + help="Whether to enable tests for experimental C++ libraries (typically Library Fundamentals TSes).", + actions=lambda experimental: [] if not experimental else [ + AddFeature('c++experimental'), + AddLinkFlag('-lc++experimental') + ]), Parameter(name='long_tests', choices=[True, False], type=bool, default=True, help="Whether to enable tests that take longer to run. This can be useful when running on a very slow device.", - feature=lambda enabled: Feature(name='long_tests') if enabled else None), + actions=lambda enabled: [] if not enabled else [ + AddFeature('long_tests') + ]), Parameter(name='enable_debug_tests', choices=[True, False], type=bool, default=True, help="Whether to enable tests that exercise the libc++ debugging mode.", - feature=lambda enabled: None if enabled else Feature(name='libcxx-no-debug-mode')), + actions=lambda enabled: [] if not enabled else [ + AddFeature('libcxx-no-debug-mode') + ]), ]