From 32117c22deb97c0be06ef073c432e45569b138c3 Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Tue, 12 Sep 2017 01:20:49 +0200 Subject: 'with_or_without' accepts bool variants Fixes #4112 This commit extends the support of the AutotoolsPackage methods `with_or_without` and `enable_or_disable` to bool-valued variants. It also defines for those functions a convenience short-cut if the activation parameter is the prefix of a spec (like in `--with-{pkg}={prefix}`). This commit also includes: * Updates to viennarna and adios accordingly: they have been modified to use `enable_or_disable` and `with_or_without` * Improved docstrings in `autotools.py`. Raise `KeyError` if name is not a variant. --- lib/spack/spack/build_systems/autotools.py | 160 ++++++++++++++++++++++++----- lib/spack/spack/test/build_systems.py | 27 ++--- 2 files changed, 151 insertions(+), 36 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/build_systems/autotools.py b/lib/spack/spack/build_systems/autotools.py index afabb90820..b78d3ed026 100644 --- a/lib/spack/spack/build_systems/autotools.py +++ b/lib/spack/spack/build_systems/autotools.py @@ -291,49 +291,161 @@ class AutotoolsPackage(PackageBase): self._if_make_target_execute('test') self._if_make_target_execute('check') - def _activate_or_not(self, active, inactive, name, active_parameters=None): + def _activate_or_not( + self, + name, + activation_word, + deactivation_word, + activation_value=None + ): + """This function contains the current implementation details of + :py:meth:`~.AutotoolsPackage.with_or_without` and + :py:meth:`~.AutotoolsPackage.enable_or_disable`. + + Args: + name (str): name of the variant that is being processed + activation_word (str): the default activation word ('with' in the + case of ``with_or_without``) + deactivation_word (str): the default deactivation word ('without' + in the case of ``with_or_without``) + activation_value (callable): callable that accepts a single + value. This value is either one of the allowed values for a + multi-valued variant or the name of a bool-valued variant. + Returns the parameter to be used when the value is activated. + + The special value 'prefix' can also be assigned and will return + ``spec[name].prefix`` as activation parameter. + + Examples: + + Given a package with: + + .. code-block:: python + + variant('foo', values=('x', 'y'), description='') + variant('bar', default=True, description='') + + calling this function like: + + .. code-block:: python + + _activate_or_not( + 'foo', 'with', 'without', activation_value='prefix' + ) + _activate_or_not('bar', 'with', 'without') + + will generate the following configuration options: + + .. code-block:: console + + --with-x= --without-y --with-bar + + for `` foo=x +bar`` + + Returns: + list of strings that corresponds to the activation/deactivation + of the variant that has been processed + + Raises: + KeyError: if name is not among known variants + """ spec = self.spec args = [] + + if activation_value == 'prefix': + activation_value = lambda x: spec[x].prefix + + # Defensively look that the name passed as argument is among + # variants + if name not in self.variants: + msg = '"{0}" is not a variant of "{1}"' + raise KeyError(msg.format(name, self.name)) + + # Create a list of pairs. Each pair includes a configuration + # option and whether or not that option is activated + if set(self.variants[name].values) == set((True, False)): + # BoolValuedVariant carry information about a single option. + # Nonetheless, for uniformity of treatment we'll package them + # in an iterable of one element. + condition = '+{name}'.format(name=name) + options = [(name, condition in spec)] + else: + condition = '{name}={value}' + options = [ + (value, condition.format(name=name, value=value) in spec) + for value in self.variants[name].values + ] + # For each allowed value in the list of values - for value in self.variants[name].values: - # Check if the value is active in the current spec - condition = '{name}={value}'.format(name=name, value=value) - activated = condition in spec + for option_value, activated in options: # Search for an override in the package for this value - override_name = '{0}_or_{1}_{2}'.format(active, inactive, value) + override_name = '{0}_or_{1}_{2}'.format( + activation_word, deactivation_word, option_value + ) line_generator = getattr(self, override_name, None) # If not available use a sensible default if line_generator is None: def _default_generator(is_activated): if is_activated: - line = '--{0}-{1}'.format(active, value) - if active_parameters is not None and active_parameters(value): # NOQA=ignore=E501 - line += '={0}'.format(active_parameters(value)) + line = '--{0}-{1}'.format( + activation_word, option_value + ) + if activation_value is not None and activation_value(option_value): # NOQA=ignore=E501 + line += '={0}'.format( + activation_value(option_value) + ) return line - return '--{0}-{1}'.format(inactive, value) + return '--{0}-{1}'.format(deactivation_word, option_value) line_generator = _default_generator args.append(line_generator(activated)) return args - def with_or_without(self, name, active_parameters=None): - """Inspects the multi-valued variant 'name' and returns the configure - arguments that activate / deactivate the selected feature. + def with_or_without(self, name, activation_value=None): + """Inspects a variant and returns the arguments that activate + or deactivate the selected feature(s) for the configure options. + + This function works on all type of variants. For bool-valued variants + it will return by default ``--with-{name}`` or ``--without-{name}``. + For other kinds of variants it will cycle over the allowed values and + return either ``--with-{value}`` or ``--without-{value}``. + + If activation_value is given, then for each possible value of the + variant, the option ``--with-{value}=activation_value(value)`` or + ``--without-{value}`` will be added depending on whether or not + ``variant=value`` is in the spec. - :param str name: name of a valid multi-valued variant - :param callable active_parameters: if present accepts a single value - and returns the parameter to be used leading to an entry of the - type '--with-{name}={parameter} + Args: + name (str): name of a valid multi-valued variant + activation_value (callable): callable that accepts a single + value and returns the parameter to be used leading to an entry + of the type ``--with-{name}={parameter}``. + + The special value 'prefix' can also be assigned and will return + ``spec[name].prefix`` as activation parameter. + + Returns: + list of arguments to configure """ - return self._activate_or_not( - 'with', 'without', name, active_parameters - ) + return self._activate_or_not(name, 'with', 'without', activation_value) + + def enable_or_disable(self, name, activation_value=None): + """Same as :py:meth:`~.AutotoolsPackage.with_or_without` but substitute + ``with`` with ``enable`` and ``without`` with ``disable``. + + Args: + name (str): name of a valid multi-valued variant + activation_value (callable): if present accepts a single value + and returns the parameter to be used leading to an entry of the + type ``--enable-{name}={parameter}`` + + The special value 'prefix' can also be assigned and will return + ``spec[name].prefix`` as activation parameter. - def enable_or_disable(self, name, active_parameters=None): - """Inspects the multi-valued variant 'name' and returns the configure - arguments that activate / deactivate the selected feature. + Returns: + list of arguments to configure """ return self._activate_or_not( - 'enable', 'disable', name, active_parameters + name, 'enable', 'disable', activation_value ) run_after('install')(PackageBase._run_default_install_time_test_callbacks) diff --git a/lib/spack/spack/test/build_systems.py b/lib/spack/spack/test/build_systems.py index 114497bccd..9960966e2f 100644 --- a/lib/spack/spack/test/build_systems.py +++ b/lib/spack/spack/test/build_systems.py @@ -53,20 +53,23 @@ class TestAutotoolsPackage(object): pkg = spack.repo.get(s) # Called without parameters - l = pkg.with_or_without('foo') - assert '--with-bar' in l - assert '--without-baz' in l - assert '--no-fee' in l + options = pkg.with_or_without('foo') + assert '--with-bar' in options + assert '--without-baz' in options + assert '--no-fee' in options def activate(value): return 'something' - l = pkg.with_or_without('foo', active_parameters=activate) - assert '--with-bar=something' in l - assert '--without-baz' in l - assert '--no-fee' in l + options = pkg.with_or_without('foo', activation_value=activate) + assert '--with-bar=something' in options + assert '--without-baz' in options + assert '--no-fee' in options - l = pkg.enable_or_disable('foo') - assert '--enable-bar' in l - assert '--disable-baz' in l - assert '--disable-fee' in l + options = pkg.enable_or_disable('foo') + assert '--enable-bar' in options + assert '--disable-baz' in options + assert '--disable-fee' in options + + options = pkg.with_or_without('bvv') + assert '--with-bvv' in options -- cgit v1.2.3-60-g2f50