summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeth R. Johnson <johnsonsr@ornl.gov>2020-03-16 14:41:19 -0400
committerGitHub <noreply@github.com>2020-03-16 11:41:19 -0700
commita8706cc89b49fadadc644cdbb7077743fcc58b4b (patch)
tree674d4644408b5cd38422ee207c0ea2cb7b200411
parent55d5adfecfdaa097724ca75bdcb4b92251697e97 (diff)
downloadspack-a8706cc89b49fadadc644cdbb7077743fcc58b4b.tar.gz
spack-a8706cc89b49fadadc644cdbb7077743fcc58b4b.tar.bz2
spack-a8706cc89b49fadadc644cdbb7077743fcc58b4b.tar.xz
spack-a8706cc89b49fadadc644cdbb7077743fcc58b4b.zip
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).
-rw-r--r--lib/spack/docs/build_systems/cmakepackage.rst17
-rw-r--r--lib/spack/spack/build_systems/cmake.py114
-rw-r--r--lib/spack/spack/test/build_systems.py38
-rw-r--r--var/spack/repos/builtin.mock/packages/cmake-client/package.py8
4 files changed, 161 insertions, 16 deletions
diff --git a/lib/spack/docs/build_systems/cmakepackage.rst b/lib/spack/docs/build_systems/cmakepackage.rst
index 0a771edad3..76e89c80b1 100644
--- a/lib/spack/docs/build_systems/cmakepackage.rst
+++ b/lib/spack/docs/build_systems/cmakepackage.rst
@@ -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
diff --git a/lib/spack/spack/build_systems/cmake.py b/lib/spack/spack/build_systems/cmake.py
index 14f33e94e6..d7da957a9d 100644
--- a/lib/spack/spack/build_systems/cmake.py
+++ b/lib/spack/spack/build_systems/cmake.py
@@ -147,33 +147,129 @@ class CMakePackage(PackageBase):
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,
diff --git a/lib/spack/spack/test/build_systems.py b/lib/spack/spack/test/build_systems.py
index 744821a04e..7ede78b7a5 100644
--- a/lib/spack/spack/test/build_systems.py
+++ b/lib/spack/spack/test/build_systems.py
@@ -181,3 +181,41 @@ class TestAutotoolsPackage(object):
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')
diff --git a/var/spack/repos/builtin.mock/packages/cmake-client/package.py b/var/spack/repos/builtin.mock/packages/cmake-client/package.py
index 286ee08086..2350259b22 100644
--- a/var/spack/repos/builtin.mock/packages/cmake-client/package.py
+++ b/var/spack/repos/builtin.mock/packages/cmake-client/package.py
@@ -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