summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGreg Becker <becker33@llnl.gov>2021-03-31 18:34:52 -0700
committerGreg Becker <becker33@llnl.gov>2021-03-31 18:38:22 -0700
commit7daf5823574dc18522f559a084095714cc9f3fb9 (patch)
tree9d75e1a8e51557cd44cb8f7acb6b656a8a77535e /lib
parentc4f0a3cf6cd2ab282f6abf20fd72fcb4739a432a (diff)
downloadspack-7daf5823574dc18522f559a084095714cc9f3fb9.tar.gz
spack-7daf5823574dc18522f559a084095714cc9f3fb9.tar.bz2
spack-7daf5823574dc18522f559a084095714cc9f3fb9.tar.xz
spack-7daf5823574dc18522f559a084095714cc9f3fb9.zip
CachedCMakePackage for using *.cmake initial config files (#19316)"
Original commit message: This feature of CMake allows packages to increase reproducibility, especially between Spack- and manual builds. It also allows packages to sidestep certain parsing bugs in extremely long ``cmake`` commands, and to avoid system limits on the length of the command line. Adding: Co-authored by: Chris White <white238@llnl.gov> This reverts commit c4f0a3cf6cd2ab282f6abf20fd72fcb4739a432a.
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/spack/build_systems/cached_cmake.py249
-rw-r--r--lib/spack/spack/pkgkit.py4
2 files changed, 253 insertions, 0 deletions
diff --git a/lib/spack/spack/build_systems/cached_cmake.py b/lib/spack/spack/build_systems/cached_cmake.py
new file mode 100644
index 0000000000..0704a4aa09
--- /dev/null
+++ b/lib/spack/spack/build_systems/cached_cmake.py
@@ -0,0 +1,249 @@
+# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+import os
+
+from llnl.util.filesystem import install, mkdirp
+import llnl.util.tty as tty
+
+from spack.build_systems.cmake import CMakePackage
+from spack.package import run_after
+
+
+def cmake_cache_path(name, value, comment=""):
+ """Generate a string for a cmake cache variable"""
+ return 'set({0} "{1}" CACHE PATH "{2}")\n'.format(name, value, comment)
+
+
+def cmake_cache_string(name, value, comment=""):
+ """Generate a string for a cmake cache variable"""
+ return 'set({0} "{1}" CACHE STRING "{2}")\n'.format(name, value, comment)
+
+
+def cmake_cache_option(name, boolean_value, comment=""):
+ """Generate a string for a cmake configuration option"""
+
+ value = "ON" if boolean_value else "OFF"
+ return 'set({0} {1} CACHE BOOL "{2}")\n'.format(name, value, comment)
+
+
+class CachedCMakePackage(CMakePackage):
+ """Specialized class for packages built using CMake initial cache.
+
+ This feature of CMake allows packages to increase reproducibility,
+ especially between Spack- and manual builds. It also allows packages to
+ sidestep certain parsing bugs in extremely long ``cmake`` commands, and to
+ avoid system limits on the length of the command line."""
+
+ phases = ['initconfig', 'cmake', 'build', 'install']
+
+ @property
+ def cache_name(self):
+ return "{0}-{1}-{2}@{3}.cmake".format(
+ self.name,
+ self.spec.architecture,
+ self.spec.compiler.name,
+ self.spec.compiler.version,
+ )
+
+ @property
+ def cache_path(self):
+ return os.path.join(self.stage.source_path, self.cache_name)
+
+ def flag_handler(self, name, flags):
+ if name in ('cflags', 'cxxflags', 'cppflags', 'fflags'):
+ return (None, None, None) # handled in the cmake cache
+ return (flags, None, None)
+
+ def initconfig_compiler_entries(self):
+ # This will tell cmake to use the Spack compiler wrappers when run
+ # through Spack, but use the underlying compiler when run outside of
+ # Spack
+ spec = self.spec
+
+ # Fortran compiler is optional
+ if "FC" in os.environ:
+ spack_fc_entry = cmake_cache_path(
+ "CMAKE_Fortran_COMPILER", os.environ['FC'])
+ system_fc_entry = cmake_cache_path(
+ "CMAKE_Fortran_COMPILER", self.compiler.fc)
+ else:
+ spack_fc_entry = "# No Fortran compiler defined in spec"
+ system_fc_entry = "# No Fortran compiler defined in spec"
+
+ entries = [
+ "#------------------{0}".format("-" * 60),
+ "# Compilers",
+ "#------------------{0}".format("-" * 60),
+ "# Compiler Spec: {0}".format(spec.compiler),
+ "#------------------{0}".format("-" * 60),
+ 'if(DEFINED ENV{SPACK_CC})\n',
+ ' ' + cmake_cache_path(
+ "CMAKE_C_COMPILER", os.environ['CC']),
+ ' ' + cmake_cache_path(
+ "CMAKE_CXX_COMPILER", os.environ['CXX']),
+ ' ' + spack_fc_entry,
+ 'else()\n',
+ ' ' + cmake_cache_path(
+ "CMAKE_C_COMPILER", self.compiler.cc),
+ ' ' + cmake_cache_path(
+ "CMAKE_CXX_COMPILER", self.compiler.cxx),
+ ' ' + system_fc_entry,
+ 'endif()\n'
+ ]
+
+ # use global spack compiler flags
+ cppflags = ' '.join(spec.compiler_flags['cppflags'])
+ if cppflags:
+ # avoid always ending up with ' ' with no flags defined
+ cppflags += ' '
+ cflags = cppflags + ' '.join(spec.compiler_flags['cflags'])
+ if cflags:
+ entries.append(cmake_cache_string("CMAKE_C_FLAGS", cflags))
+ cxxflags = cppflags + ' '.join(spec.compiler_flags['cxxflags'])
+ if cxxflags:
+ entries.append(cmake_cache_string("CMAKE_CXX_FLAGS", cxxflags))
+ fflags = ' '.join(spec.compiler_flags['fflags'])
+ if fflags:
+ entries.append(cmake_cache_string("CMAKE_Fortran_FLAGS", fflags))
+
+ # Override XL compiler family
+ familymsg = ("Override to proper compiler family for XL")
+ if "xlf" in (self.compiler.fc or ''): # noqa: F821
+ entries.append(cmake_cache_string(
+ "CMAKE_Fortran_COMPILER_ID", "XL",
+ familymsg))
+ if "xlc" in self.compiler.cc: # noqa: F821
+ entries.append(cmake_cache_string(
+ "CMAKE_C_COMPILER_ID", "XL",
+ familymsg))
+ if "xlC" in self.compiler.cxx: # noqa: F821
+ entries.append(cmake_cache_string(
+ "CMAKE_CXX_COMPILER_ID", "XL",
+ familymsg))
+
+ return entries
+
+ def initconfig_mpi_entries(self):
+ spec = self.spec
+
+ if "+mpi" not in spec:
+ return []
+
+ entries = [
+ "#------------------{0}".format("-" * 60),
+ "# MPI",
+ "#------------------{0}\n".format("-" * 60),
+ ]
+
+ entries.append(cmake_cache_path("MPI_C_COMPILER",
+ spec['mpi'].mpicc))
+ entries.append(cmake_cache_path("MPI_CXX_COMPILER",
+ spec['mpi'].mpicxx))
+ entries.append(cmake_cache_path("MPI_Fortran_COMPILER",
+ spec['mpi'].mpifc))
+
+ # Check for slurm
+ using_slurm = False
+ slurm_checks = ['+slurm',
+ 'schedulers=slurm',
+ 'process_managers=slurm']
+ if any(spec['mpi'].satisfies(variant) for variant in slurm_checks):
+ using_slurm = True
+
+ # Determine MPIEXEC
+ if using_slurm:
+ if spec['mpi'].external:
+ # Heuristic until we have dependents on externals
+ mpiexec = '/usr/bin/srun'
+ else:
+ mpiexec = os.path.join(spec['slurm'].prefix.bin, 'srun')
+ else:
+ mpiexec = os.path.join(spec['mpi'].prefix.bin, 'mpirun')
+ if not os.path.exists(mpiexec):
+ mpiexec = os.path.join(spec['mpi'].prefix.bin, 'mpiexec')
+
+ if not os.path.exists(mpiexec):
+ msg = "Unable to determine MPIEXEC, %s tests may fail" % self.name
+ entries.append("# {0}\n".format(msg))
+ tty.warn(msg)
+ else:
+ # starting with cmake 3.10, FindMPI expects MPIEXEC_EXECUTABLE
+ # vs the older versions which expect MPIEXEC
+ if self.spec["cmake"].satisfies('@3.10:'):
+ entries.append(cmake_cache_path("MPIEXEC_EXECUTABLE",
+ mpiexec))
+ else:
+ entries.append(cmake_cache_path("MPIEXEC", mpiexec))
+
+ # Determine MPIEXEC_NUMPROC_FLAG
+ if using_slurm:
+ entries.append(cmake_cache_string("MPIEXEC_NUMPROC_FLAG", "-n"))
+ else:
+ entries.append(cmake_cache_string("MPIEXEC_NUMPROC_FLAG", "-np"))
+
+ return entries
+
+ def initconfig_hardware_entries(self):
+ spec = self.spec
+
+ entries = [
+ "#------------------{0}".format("-" * 60),
+ "# Hardware",
+ "#------------------{0}\n".format("-" * 60),
+ ]
+
+ if '+cuda' in spec:
+ entries.append("#------------------{0}".format("-" * 30))
+ entries.append("# Cuda")
+ entries.append("#------------------{0}\n".format("-" * 30))
+
+ cudatoolkitdir = spec['cuda'].prefix
+ entries.append(cmake_cache_path("CUDA_TOOLKIT_ROOT_DIR",
+ cudatoolkitdir))
+ cudacompiler = "${CUDA_TOOLKIT_ROOT_DIR}/bin/nvcc"
+ entries.append(cmake_cache_path("CMAKE_CUDA_COMPILER",
+ cudacompiler))
+
+ if "+mpi" in spec:
+ entries.append(cmake_cache_path("CMAKE_CUDA_HOST_COMPILER",
+ "${MPI_CXX_COMPILER}"))
+ else:
+ entries.append(cmake_cache_path("CMAKE_CUDA_HOST_COMPILER",
+ "${CMAKE_CXX_COMPILER}"))
+
+ return entries
+
+ def std_initconfig_entries(self):
+ return [
+ "#------------------{0}".format("-" * 60),
+ "# !!!! This is a generated file, edit at own risk !!!!",
+ "#------------------{0}".format("-" * 60),
+ "# CMake executable path: {0}".format(
+ self.spec['cmake'].command.path),
+ "#------------------{0}\n".format("-" * 60),
+ ]
+
+ def initconfig(self, spec, prefix):
+ cache_entries = (self.std_initconfig_entries() +
+ self.initconfig_compiler_entries() +
+ self.initconfig_mpi_entries() +
+ self.initconfig_hardware_entries() +
+ self.initconfig_package_entries())
+
+ with open(self.cache_name, 'w') as f:
+ for entry in cache_entries:
+ f.write('%s\n' % entry)
+ f.write('\n')
+
+ @property
+ def std_cmake_args(self):
+ args = super(CachedCMakePackage, self).std_cmake_args
+ args.extend(['-C', self.cache_path])
+ return args
+
+ @run_after('install')
+ def install_cmake_cache(self):
+ mkdirp(self.spec.prefix.share.cmake)
+ install(self.cache_path, self.spec.prefix.share.cmake)
diff --git a/lib/spack/spack/pkgkit.py b/lib/spack/spack/pkgkit.py
index 133a581e20..bc44d0b7b2 100644
--- a/lib/spack/spack/pkgkit.py
+++ b/lib/spack/spack/pkgkit.py
@@ -19,6 +19,10 @@ from spack.build_systems.makefile import MakefilePackage
from spack.build_systems.aspell_dict import AspellDictPackage
from spack.build_systems.autotools import AutotoolsPackage
from spack.build_systems.cmake import CMakePackage
+from spack.build_systems.cached_cmake import (
+ CachedCMakePackage, cmake_cache_option, cmake_cache_path,
+ cmake_cache_string
+)
from spack.build_systems.cuda import CudaPackage
from spack.build_systems.oneapi import IntelOneApiPackage
from spack.build_systems.oneapi import IntelOneApiLibraryPackage