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
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
def cmake_args(self):
|
def cmake_args(self):
|
||||||
args = []
|
args = [
|
||||||
|
'-DWHATEVER:STRING=somevalue',
|
||||||
if '+hdf5' in self.spec:
|
self.define('ENABLE_BROKEN_FEATURE', False),
|
||||||
args.append('-DDETECT_HDF5=ON')
|
self.define_from_variant('DETECT_HDF5', 'hdf5'),
|
||||||
else:
|
self.define_from_variant('THREADS'), # True if +threads
|
||||||
args.append('-DDETECT_HDF5=OFF')
|
]
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
@ -147,33 +147,129 @@ def _std_args(pkg):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
build_type = 'RelWithDebInfo'
|
build_type = 'RelWithDebInfo'
|
||||||
|
|
||||||
|
define = CMakePackage.define
|
||||||
args = [
|
args = [
|
||||||
'-G', generator,
|
'-G', generator,
|
||||||
'-DCMAKE_INSTALL_PREFIX:PATH={0}'.format(pkg.prefix),
|
define('CMAKE_INSTALL_PREFIX', pkg.prefix),
|
||||||
'-DCMAKE_BUILD_TYPE:STRING={0}'.format(build_type),
|
define('CMAKE_BUILD_TYPE', build_type),
|
||||||
]
|
]
|
||||||
|
|
||||||
if primary_generator == 'Unix Makefiles':
|
if primary_generator == 'Unix Makefiles':
|
||||||
args.append('-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON')
|
args.append(define('CMAKE_VERBOSE_MAKEFILE', True))
|
||||||
|
|
||||||
if platform.mac_ver()[0]:
|
if platform.mac_ver()[0]:
|
||||||
args.extend([
|
args.extend([
|
||||||
'-DCMAKE_FIND_FRAMEWORK:STRING=LAST',
|
define('CMAKE_FIND_FRAMEWORK', "LAST"),
|
||||||
'-DCMAKE_FIND_APPBUNDLE:STRING=LAST'
|
define('CMAKE_FIND_APPBUNDLE', "LAST"),
|
||||||
])
|
])
|
||||||
|
|
||||||
# Set up CMake rpath
|
# Set up CMake rpath
|
||||||
args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=FALSE')
|
args.extend([
|
||||||
rpaths = ';'.join(spack.build_environment.get_rpaths(pkg))
|
define('CMAKE_INSTALL_RPATH_USE_LINK_PATH', False),
|
||||||
args.append('-DCMAKE_INSTALL_RPATH:STRING={0}'.format(rpaths))
|
define('CMAKE_INSTALL_RPATH',
|
||||||
|
spack.build_environment.get_rpaths(pkg)),
|
||||||
|
])
|
||||||
# CMake's find_package() looks in CMAKE_PREFIX_PATH first, help CMake
|
# CMake's find_package() looks in CMAKE_PREFIX_PATH first, help CMake
|
||||||
# to find immediate link dependencies in right places:
|
# to find immediate link dependencies in right places:
|
||||||
deps = [d.prefix for d in
|
deps = [d.prefix for d in
|
||||||
pkg.spec.dependencies(deptype=('build', 'link'))]
|
pkg.spec.dependencies(deptype=('build', 'link'))]
|
||||||
deps = filter_system_paths(deps)
|
deps = filter_system_paths(deps)
|
||||||
args.append('-DCMAKE_PREFIX_PATH:STRING={0}'.format(';'.join(deps)))
|
args.append(define('CMAKE_PREFIX_PATH', deps))
|
||||||
return args
|
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):
|
def flags_to_build_system_args(self, flags):
|
||||||
"""Produces a list of all command line arguments to pass the specified
|
"""Produces a list of all command line arguments to pass the specified
|
||||||
compiler flags to cmake. Note CMAKE does not have a cppflags option,
|
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-bar' in options
|
||||||
assert '--without-baz' in options
|
assert '--without-baz' in options
|
||||||
assert '--no-fee' 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')
|
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
|
callback_counter = 0
|
||||||
|
|
||||||
flipped = False
|
flipped = False
|
||||||
|
Loading…
Reference in New Issue
Block a user