diff options
-rw-r--r-- | lib/spack/docs/build_systems/cmakepackage.rst | 17 | ||||
-rw-r--r-- | lib/spack/spack/build_systems/cmake.py | 114 | ||||
-rw-r--r-- | lib/spack/spack/test/build_systems.py | 38 | ||||
-rw-r--r-- | var/spack/repos/builtin.mock/packages/cmake-client/package.py | 8 |
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 |