CMakePackage: convert variants to CMake arguments (#14376)
Add a 'define_from_variant` helper function to CMake-based Spack packages to convert package variants into CMake arguments. For example: args.append('-DFOO=%s' % ('ON' if '+foo' in self.spec else 'OFF')) can be replaced with: args.append(self.define_from_variant('foo')) The following conversions are handled automatically: * Flag variants will be converted to CMake booleans * Multivalued variants will be converted to semicolon-separated strings * Other variant values are converted to CMake string arguments This also adds a 'define' helper method to convert any variable to a CMake argument. It has the same conversion rules as 'define_from_variant' (but operates directly on values rather than requiring the user to supply the name of a package variant).
This commit is contained in:
parent
55d5adfecf
commit
a8706cc89b
@ -128,17 +128,20 @@ Adding flags to cmake
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To add additional flags to the ``cmake`` call, simply override the
|
||||
``cmake_args`` function:
|
||||
``cmake_args`` function. The following example defines values for the flags
|
||||
``WHATEVER``, ``ENABLE_BROKEN_FEATURE``, ``DETECT_HDF5``, and ``THREADS`` with
|
||||
and without the :py:meth:`~.CMakePackage.define` and
|
||||
:py:meth:`~.CMakePackage.define_from_variant` helper functions:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def cmake_args(self):
|
||||
args = []
|
||||
|
||||
if '+hdf5' in self.spec:
|
||||
args.append('-DDETECT_HDF5=ON')
|
||||
else:
|
||||
args.append('-DDETECT_HDF5=OFF')
|
||||
args = [
|
||||
'-DWHATEVER:STRING=somevalue',
|
||||
self.define('ENABLE_BROKEN_FEATURE', False),
|
||||
self.define_from_variant('DETECT_HDF5', 'hdf5'),
|
||||
self.define_from_variant('THREADS'), # True if +threads
|
||||
]
|
||||
|
||||
return args
|
||||
|
||||
|
@ -147,33 +147,129 @@ def _std_args(pkg):
|
||||
except KeyError:
|
||||
build_type = 'RelWithDebInfo'
|
||||
|
||||
define = CMakePackage.define
|
||||
args = [
|
||||
'-G', generator,
|
||||
'-DCMAKE_INSTALL_PREFIX:PATH={0}'.format(pkg.prefix),
|
||||
'-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type),
|
||||
define('CMAKE_INSTALL_PREFIX', pkg.prefix),
|
||||
define('CMAKE_BUILD_TYPE', build_type),
|
||||
]
|
||||
|
||||
if primary_generator == 'Unix Makefiles':
|
||||
args.append('-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON')
|
||||
args.append(define('CMAKE_VERBOSE_MAKEFILE', True))
|
||||
|
||||
if platform.mac_ver()[0]:
|
||||
args.extend([
|
||||
'-DCMAKE_FIND_FRAMEWORK:STRING=LAST',
|
||||
'-DCMAKE_FIND_APPBUNDLE:STRING=LAST'
|
||||
define('CMAKE_FIND_FRAMEWORK', "LAST"),
|
||||
define('CMAKE_FIND_APPBUNDLE', "LAST"),
|
||||
])
|
||||
|
||||
# Set up CMake rpath
|
||||
args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=FALSE')
|
||||
rpaths = ';'.join(spack.build_environment.get_rpaths(pkg))
|
||||
args.append('-DCMAKE_INSTALL_RPATH:STRING={0}'.format(rpaths))
|
||||
args.extend([
|
||||
define('CMAKE_INSTALL_RPATH_USE_LINK_PATH', False),
|
||||
define('CMAKE_INSTALL_RPATH',
|
||||
spack.build_environment.get_rpaths(pkg)),
|
||||
])
|
||||
# CMake's find_package() looks in CMAKE_PREFIX_PATH first, help CMake
|
||||
# to find immediate link dependencies in right places:
|
||||
deps = [d.prefix for d in
|
||||
pkg.spec.dependencies(deptype=('build', 'link'))]
|
||||
deps = filter_system_paths(deps)
|
||||
args.append('-DCMAKE_PREFIX_PATH:STRING={0}'.format(';'.join(deps)))
|
||||
args.append(define('CMAKE_PREFIX_PATH', deps))
|
||||
return args
|
||||
|
||||
@staticmethod
|
||||
def define(cmake_var, value):
|
||||
"""Return a CMake command line argument that defines a variable.
|
||||
|
||||
The resulting argument will convert boolean values to OFF/ON
|
||||
and lists/tuples to CMake semicolon-separated string lists. All other
|
||||
values will be interpreted as strings.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[define('BUILD_SHARED_LIBS', True),
|
||||
define('CMAKE_CXX_STANDARD', 14),
|
||||
define('swr', ['avx', 'avx2'])]
|
||||
|
||||
will generate the following configuration options:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
["-DBUILD_SHARED_LIBS:BOOL=ON",
|
||||
"-DCMAKE_CXX_STANDARD:STRING=14",
|
||||
"-DSWR:STRING=avx;avx2]
|
||||
|
||||
"""
|
||||
# Create a list of pairs. Each pair includes a configuration
|
||||
# option and whether or not that option is activated
|
||||
if isinstance(value, bool):
|
||||
kind = 'BOOL'
|
||||
value = "ON" if value else "OFF"
|
||||
else:
|
||||
kind = 'STRING'
|
||||
if isinstance(value, (list, tuple)):
|
||||
value = ";".join(str(v) for v in value)
|
||||
else:
|
||||
value = str(value)
|
||||
|
||||
return "".join(["-D", cmake_var, ":", kind, "=", value])
|
||||
|
||||
def define_from_variant(self, cmake_var, variant=None):
|
||||
"""Return a CMake command line argument from the given variant's value.
|
||||
|
||||
The optional ``variant`` argument defaults to the lower-case transform
|
||||
of ``cmake_var``.
|
||||
|
||||
This utility function is similar to
|
||||
:py:meth:`~.AutotoolsPackage.with_or_without`.
|
||||
|
||||
Examples:
|
||||
|
||||
Given a package with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
variant('cxxstd', default='11', values=('11', '14'),
|
||||
multi=False, description='')
|
||||
variant('shared', default=True, description='')
|
||||
variant('swr', values=any_combination_of('avx', 'avx2'),
|
||||
description='')
|
||||
|
||||
calling this function like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[define_from_variant('BUILD_SHARED_LIBS', 'shared'),
|
||||
define_from_variant('CMAKE_CXX_STANDARD', 'cxxstd'),
|
||||
define_from_variant('SWR')]
|
||||
|
||||
will generate the following configuration options:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
["-DBUILD_SHARED_LIBS:BOOL=ON",
|
||||
"-DCMAKE_CXX_STANDARD:STRING=14",
|
||||
"-DSWR:STRING=avx;avx2]
|
||||
|
||||
for ``<spec-name> cxxstd=14 +shared swr=avx,avx2``
|
||||
"""
|
||||
|
||||
if variant is None:
|
||||
variant = cmake_var.lower()
|
||||
|
||||
if variant not in self.variants:
|
||||
raise KeyError(
|
||||
'"{0}" is not a variant of "{1}"'.format(variant, self.name))
|
||||
|
||||
value = self.spec.variants[variant].value
|
||||
if isinstance(value, (tuple, list)):
|
||||
# Sort multi-valued variants for reproducibility
|
||||
value = sorted(value)
|
||||
|
||||
return self.define(cmake_var, value)
|
||||
|
||||
def flags_to_build_system_args(self, flags):
|
||||
"""Produces a list of all command line arguments to pass the specified
|
||||
compiler flags to cmake. Note CMAKE does not have a cppflags option,
|
||||
|
@ -181,3 +181,41 @@ def test_none_is_allowed(self):
|
||||
assert '--without-bar' in options
|
||||
assert '--without-baz' in options
|
||||
assert '--no-fee' in options
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('config', 'mock_packages')
|
||||
class TestCMakePackage(object):
|
||||
|
||||
def test_define(self):
|
||||
s = Spec('cmake-client')
|
||||
s.concretize()
|
||||
pkg = spack.repo.get(s)
|
||||
|
||||
for cls in (list, tuple):
|
||||
arg = pkg.define('MULTI', cls(['right', 'up']))
|
||||
assert arg == '-DMULTI:STRING=right;up'
|
||||
|
||||
arg = pkg.define('ENABLE_TRUTH', False)
|
||||
assert arg == '-DENABLE_TRUTH:BOOL=OFF'
|
||||
arg = pkg.define('ENABLE_TRUTH', True)
|
||||
assert arg == '-DENABLE_TRUTH:BOOL=ON'
|
||||
|
||||
arg = pkg.define('SINGLE', 'red')
|
||||
assert arg == '-DSINGLE:STRING=red'
|
||||
|
||||
def test_define_from_variant(self):
|
||||
s = Spec('cmake-client multi=up,right ~truthy single=red')
|
||||
s.concretize()
|
||||
pkg = spack.repo.get(s)
|
||||
|
||||
arg = pkg.define_from_variant('MULTI')
|
||||
assert arg == '-DMULTI:STRING=right;up'
|
||||
|
||||
arg = pkg.define_from_variant('ENABLE_TRUTH', 'truthy')
|
||||
assert arg == '-DENABLE_TRUTH:BOOL=OFF'
|
||||
|
||||
arg = pkg.define_from_variant('SINGLE')
|
||||
assert arg == '-DSINGLE:STRING=red'
|
||||
|
||||
with pytest.raises(KeyError, match="not a variant"):
|
||||
pkg.define_from_variant('NONEXISTENT')
|
||||
|
@ -21,6 +21,14 @@ class CmakeClient(CMakePackage):
|
||||
|
||||
version('1.0', '4cb3ff35b2472aae70f542116d616e63')
|
||||
|
||||
variant(
|
||||
'multi', description='',
|
||||
values=any_combination_of('up', 'right', 'back').with_default('up')
|
||||
)
|
||||
variant('single', description='', default='blue',
|
||||
values=('blue', 'red', 'green'), multi=False)
|
||||
variant('truthy', description='', default=True)
|
||||
|
||||
callback_counter = 0
|
||||
|
||||
flipped = False
|
||||
|
Loading…
Reference in New Issue
Block a user