spack/var/spack/repos/builtin/packages/kokkos/package.py
Cody Balos e60e743843
kokkos and kokkos-kernels: add new versions (#33301)
* kokkos: add version 3.7.00
* kokkos-kernels: add versions 3.6.01 and 3.7.00
* add correct kokkos dependence
2022-10-20 11:50:59 -07:00

392 lines
16 KiB
Python

# Copyright 2013-2022 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.path
from llnl.util import tty
from spack.package import *
class Kokkos(CMakePackage, CudaPackage, ROCmPackage):
"""Kokkos implements a programming model in C++ for writing performance
portable applications targeting all major HPC platforms."""
homepage = "https://github.com/kokkos/kokkos"
git = "https://github.com/kokkos/kokkos.git"
url = "https://github.com/kokkos/kokkos/archive/3.6.00.tar.gz"
tags = ["e4s"]
test_requires_compiler = True
maintainers = ["janciesko", "crtrott"]
version("master", branch="master")
version("develop", branch="develop")
version("3.7.00", sha256="62e3f9f51c798998f6493ed36463f66e49723966286ef70a9dcba329b8443040")
version("3.6.01", sha256="1b80a70c5d641da9fefbbb652e857d7c7a76a0ebad1f477c253853e209deb8db")
version("3.6.00", sha256="53b11fffb53c5d48da5418893ac7bc814ca2fde9c86074bdfeaa967598c918f4")
version("3.5.00", sha256="748f06aed63b1e77e3653cd2f896ef0d2c64cb2e2d896d9e5a57fec3ff0244ff")
version("3.4.01", sha256="146d5e233228e75ef59ca497e8f5872d9b272cb93e8e9cdfe05ad34a23f483d1")
version("3.4.00", sha256="2e4438f9e4767442d8a55e65d000cc9cde92277d415ab4913a96cd3ad901d317")
version("3.3.01", sha256="4919b00bb7b6eb80f6c335a32f98ebe262229d82e72d3bae6dd91aaf3d234c37")
version("3.3.00", sha256="170b9deaa1943185e928f8fcb812cd4593a07ed7d220607467e8f0419e147295")
version("3.2.01", sha256="9e27a3d8f81559845e190d60f277d84d6f558412a3df3301d9545e91373bcaf1")
version("3.2.00", sha256="05e1b4dd1ef383ca56fe577913e1ff31614764e65de6d6f2a163b2bddb60b3e9")
version("3.1.01", sha256="ff5024ebe8570887d00246e2793667e0d796b08c77a8227fe271127d36eec9dd")
version("3.1.00", sha256="b935c9b780e7330bcb80809992caa2b66fd387e3a1c261c955d622dae857d878")
version("3.0.00", sha256="c00613d0194a4fbd0726719bbed8b0404ed06275f310189b3493f5739042a92b")
depends_on("cmake@3.16:", type="build")
devices_variants = {
"cuda": [False, "Whether to build CUDA backend"],
"openmp": [False, "Whether to build OpenMP backend"],
"pthread": [False, "Whether to build Pthread backend"],
"serial": [True, "Whether to build serial backend"],
"rocm": [False, "Whether to build HIP backend"],
"sycl": [False, "Whether to build the SYCL backend"],
"openmptarget": [False, "Whether to build the OpenMPTarget backend"],
}
conflicts("+rocm", when="@:3.0")
conflicts("+sycl", when="@:3.3")
conflicts("+openmptarget", when="@:3.5")
# https://github.com/spack/spack/issues/29052
conflicts("@:3.5 +sycl", when="%dpcpp@2022:")
conflicts("@:3.5 +sycl", when="%oneapi@2022:")
tpls_variants = {
"hpx": [False, "Whether to enable the HPX library"],
"hwloc": [False, "Whether to enable the HWLOC library"],
"numactl": [False, "Whether to enable the LIBNUMA library"],
"memkind": [False, "Whether to enable the MEMKIND library"],
}
options_variants = {
"aggressive_vectorization": [False, "Aggressively vectorize loops"],
"compiler_warnings": [False, "Print all compiler warnings"],
"cuda_constexpr": [False, "Activate experimental constexpr features"],
"cuda_lambda": [False, "Activate experimental lambda features"],
"cuda_ldg_intrinsic": [False, "Use CUDA LDG intrinsics"],
"cuda_relocatable_device_code": [False, "Enable RDC for CUDA"],
"cuda_uvm": [False, "Enable unified virtual memory (UVM) for CUDA"],
"debug": [False, "Activate extra debug features - may increase compiletimes"],
"debug_bounds_check": [False, "Use bounds checking - will increase runtime"],
"debug_dualview_modify_check": [False, "Debug check on dual views"],
"deprecated_code": [False, "Whether to enable deprecated code"],
"examples": [False, "Whether to build OpenMP backend"],
"explicit_instantiation": [False, "Explicitly instantiate template types"],
"hpx_async_dispatch": [False, "Whether HPX supports asynchronous dispath"],
"profiling": [True, "Create bindings for profiling tools"],
"tuning": [False, "Create bindings for tuning tools"],
"profiling_load_print": [False, "Print which profiling tools got loaded"],
"qthread": [False, "Eenable the QTHREAD library"],
"tests": [False, "Build for tests"],
}
spack_micro_arch_map = {
"thunderx2": "THUNDERX2",
"zen": "ZEN",
"zen2": "ZEN2",
"steamroller": "KAVERI",
"excavator": "CARIZO",
"power7": "POWER7",
"power8": "POWER8",
"power9": "POWER9",
"power8le": "POWER8",
"power9le": "POWER9",
"sandybridge": "SNB",
"haswell": "HSW",
"mic_knl": "KNL",
"cannonlake": "SKX",
"cascadelake": "SKX",
"westmere": "WSM",
"ivybridge": "SNB",
"broadwell": "BDW",
# @AndrewGaspar: Kokkos does not have an arch for plain-skylake - only
# for Skylake-X (i.e. Xeon). For now, I'm mapping this to Broadwell
# until Kokkos learns to optimize for SkyLake without the AVX-512
# extensions. SkyLake with AVX-512 will still be optimized using the
# separate `skylake_avx512` arch.
"skylake": "BDW",
"icelake": "SKX",
"skylake_avx512": "SKX",
}
spack_cuda_arch_map = {
"30": "kepler30",
"32": "kepler32",
"35": "kepler35",
"37": "kepler37",
"50": "maxwell50",
"52": "maxwell52",
"53": "maxwell53",
"60": "pascal60",
"61": "pascal61",
"70": "volta70",
"72": "volta72",
"75": "turing75",
"80": "ampere80",
"86": "ampere86",
}
cuda_arches = spack_cuda_arch_map.values()
conflicts("+cuda", when="cuda_arch=none")
amdgpu_arch_map = {
"gfx900": "vega900",
"gfx906": "vega906",
"gfx908": "vega908",
"gfx90a": "vega90A",
}
amd_support_conflict_msg = (
"{0} is not supported; "
"Kokkos supports the following AMD GPU targets: " + ", ".join(amdgpu_arch_map.keys())
)
for arch in ROCmPackage.amdgpu_targets:
if arch not in amdgpu_arch_map:
conflicts(
"+rocm",
when="amdgpu_target={0}".format(arch),
msg=amd_support_conflict_msg.format(arch),
)
intel_gpu_arches = (
"intel_gen",
"intel_gen9",
"intel_gen11",
"intel_gen12lp",
"intel_dg1",
"intel_xehp",
"intel_pvc",
)
variant(
"intel_gpu_arch",
default="none",
values=("none",) + intel_gpu_arches,
description="Intel GPU architecture",
)
devices_values = list(devices_variants.keys())
for dev in devices_variants:
dflt, desc = devices_variants[dev]
variant(dev, default=dflt, description=desc)
conflicts("+cuda", when="+rocm", msg="CUDA and ROCm are not compatible in Kokkos.")
options_values = list(options_variants.keys())
for opt in options_values:
if "cuda" in opt:
conflicts("+%s" % opt, when="~cuda", msg="Must enable CUDA to use %s" % opt)
dflt, desc = options_variants[opt]
variant(opt, default=dflt, description=desc)
tpls_values = list(tpls_variants.keys())
for tpl in tpls_values:
dflt, desc = tpls_variants[tpl]
variant(tpl, default=dflt, description=desc)
depends_on(tpl, when="+%s" % tpl)
variant("wrapper", default=False, description="Use nvcc-wrapper for CUDA build")
depends_on("kokkos-nvcc-wrapper", when="+wrapper")
depends_on("kokkos-nvcc-wrapper@develop", when="@develop+wrapper")
depends_on("kokkos-nvcc-wrapper@master", when="@master+wrapper")
conflicts("+wrapper", when="~cuda")
stds = ["11", "14", "17", "20"]
# TODO: This should be named cxxstd for consistency with other packages
variant("std", default="14", values=stds, multi=False)
variant("pic", default=False, description="Build position independent code")
# nvcc does not currently work with C++17 or C++20
conflicts("+cuda", when="std=17 ^cuda@:10")
conflicts("+cuda", when="std=20")
# SYCL and OpenMPTarget require C++17 or higher
for stdver in stds[: stds.index("17")]:
conflicts("+sycl", when="std={0}".format(stdver), msg="SYCL requires C++17 or higher")
conflicts(
"+openmptarget",
when="std={0}".format(stdver),
msg="OpenMPTarget requires C++17 or higher",
)
# HPX should use the same C++ standard
for std in stds:
depends_on("hpx cxxstd={0}".format(std), when="+hpx std={0}".format(std))
# HPX version constraints
depends_on("hpx@:1.6", when="@:3.5 +hpx")
depends_on("hpx@1.7:", when="@3.6: +hpx")
# Patches
patch("hpx_profiling_fences.patch", when="@3.5.00 +hpx")
variant("shared", default=True, description="Build shared libraries")
@classmethod
def get_microarch(cls, target):
"""Get the Kokkos microarch name for a Spack target (spec.target)."""
smam = cls.spack_micro_arch_map
# Find closest ancestor that has a known microarch optimization
if target.name not in smam:
for target in target.ancestors:
if target.name in smam:
break
else:
# No known microarch optimizatinos
return None
return smam[target.name]
def append_args(self, cmake_prefix, cmake_options, spack_options):
variant_to_cmake_option = {"rocm": "hip"}
for variant_name in cmake_options:
opt = variant_to_cmake_option.get(variant_name, variant_name)
optname = "Kokkos_%s_%s" % (cmake_prefix, opt.upper())
# Explicitly enable or disable
option = self.define_from_variant(optname, variant_name)
if option not in spack_options:
spack_options.append(option)
def setup_dependent_package(self, module, dependent_spec):
try:
self.spec.kokkos_cxx = self.spec["kokkos-nvcc-wrapper"].kokkos_cxx
except Exception:
self.spec.kokkos_cxx = spack_cxx
def cmake_args(self):
spec = self.spec
from_variant = self.define_from_variant
if spec.satisfies("~wrapper+cuda") and not (
spec.satisfies("%clang") or spec.satisfies("%cce")
):
raise InstallError("Kokkos requires +wrapper when using +cuda" "without clang")
options = [
from_variant("CMAKE_POSITION_INDEPENDENT_CODE", "pic"),
from_variant("CMAKE_CXX_STANDARD", "std"),
from_variant("BUILD_SHARED_LIBS", "shared"),
]
spack_microarches = []
if "+cuda" in spec:
# this is a list
for cuda_arch in spec.variants["cuda_arch"].value:
if not cuda_arch == "none":
kokkos_arch_name = self.spack_cuda_arch_map[cuda_arch]
spack_microarches.append(kokkos_arch_name)
kokkos_microarch_name = self.get_microarch(spec.target)
if kokkos_microarch_name:
spack_microarches.append(kokkos_microarch_name)
if "+rocm" in spec:
for amdgpu_target in spec.variants["amdgpu_target"].value:
if amdgpu_target != "none":
if amdgpu_target in self.amdgpu_arch_map:
spack_microarches.append(self.amdgpu_arch_map[amdgpu_target])
else:
# Note that conflict declarations should prevent
# choosing an unsupported AMD GPU target
raise SpackError("Unsupported target: {0}".format(amdgpu_target))
if self.spec.variants["intel_gpu_arch"].value != "none":
spack_microarches.append(self.spec.variants["intel_gpu_arch"].value)
for arch in spack_microarches:
options.append(self.define("Kokkos_ARCH_" + arch.upper(), True))
self.append_args("ENABLE", self.devices_values, options)
self.append_args("ENABLE", self.options_values, options)
self.append_args("ENABLE", self.tpls_values, options)
for tpl in self.tpls_values:
if spec.variants[tpl].value:
options.append(self.define(tpl + "_DIR", spec[tpl].prefix))
if "+rocm" in self.spec:
options.append(self.define("CMAKE_CXX_COMPILER", self.spec["hip"].hipcc))
elif "+wrapper" in self.spec:
options.append(
self.define("CMAKE_CXX_COMPILER", self.spec["kokkos-nvcc-wrapper"].kokkos_cxx)
)
return options
test_script_relative_path = join_path("scripts", "spack_test")
# TODO: Replace this method and its 'get' use for cmake path with
# join_path(self.spec['cmake'].prefix.bin, 'cmake') once stand-alone
# tests can access build dependencies through self.spec['cmake'].
def cmake_bin(self, set=True):
"""(Hack) Set/get cmake dependency path."""
filepath = join_path(self.install_test_root, "cmake_bin_path.txt")
if set:
with open(filepath, "w") as out_file:
cmake_bin = join_path(self.spec["cmake"].prefix.bin, "cmake")
out_file.write("{0}\n".format(cmake_bin))
elif os.path.isfile(filepath):
with open(filepath, "r") as in_file:
return in_file.read().strip()
@run_after("install")
def setup_build_tests(self):
# Skip if unsupported version
cmake_source_path = join_path(self.stage.source_path, self.test_script_relative_path)
if not os.path.exists(cmake_source_path):
return
"""Copy test."""
cmake_out_path = join_path(self.test_script_relative_path, "out")
cmake_args = [
cmake_source_path,
"-DSPACK_PACKAGE_SOURCE_DIR:PATH={0}".format(self.stage.source_path),
"-DSPACK_PACKAGE_TEST_ROOT_DIR:PATH={0}".format(
join_path(self.install_test_root, cmake_out_path)
),
"-DSPACK_PACKAGE_INSTALL_DIR:PATH={0}".format(self.prefix),
]
cmake(*cmake_args)
self.cache_extra_test_sources(cmake_out_path)
self.cmake_bin(set=True)
def build_tests(self, cmake_path):
"""Build test."""
cmake_bin = self.cmake_bin(set=False)
if not cmake_bin:
tty.msg("Skipping kokkos test: cmake_bin_path.txt not found")
return
cmake_args = [cmake_path, "-DEXECUTABLE_OUTPUT_PATH=" + cmake_path]
if not self.run_test(cmake_bin, options=cmake_args, purpose="Generate the Makefile"):
tty.warn("Skipping kokkos test: failed to generate Makefile")
return
if not self.run_test("make", purpose="Build test software"):
tty.warn("Skipping kokkos test: failed to build test")
def run_tests(self, cmake_path):
"""Run test."""
if not self.run_test(
"make", options=[cmake_path, "test"], purpose="Checking ability to execute."
):
tty.warn("Failed to run kokkos test")
def test(self):
# Skip if unsupported version
cmake_path = join_path(
self.test_suite.current_test_cache_dir, self.test_script_relative_path, "out"
)
if not os.path.exists(cmake_path):
tty.warn("Skipping smoke tests: {0} is missing".format(cmake_path))
return
self.build_tests(cmake_path)
self.run_tests(cmake_path)