Repository: spack/spack

Issue:
umpire: patching not exported cmake targets from blt
I addressed this problem getting a `-lcuda_runtime not found` error when linking with umpire (using CMake).

After investigating the problem, I discovered that `cuda_runtime` is a CMake target of BLT acting as a sort of "placeholder" for the cuda libraries (it is not the only one). Going on in this investigation, I found out that starting from `blt@0.4.0` the management of these targets has changed.

https://github.com/spack/spack/blob/06c8fdafd4f139d8e92d1c9a06e3762b8329ed7e/var/spack/repos/builtin/packages/blt/package.py#L21-L24

And it has to be managed as reported in the [documentation](https://llnl-blt.readthedocs.io/en/develop/tutorial/exporting_targets.html) cited in the comment.

Then, I realized that on umpire `develop` branch it has been solved by the PR https://github.com/LLNL/Umpire/pull/541, but the current production release v5.0.1 does not have it.

For the above reasons I opted for applying the specific patch from the commit on the develop branch:
- for all versions of umpire up to (included) 5.0.1, because in the next release the fix should be in,
- but just for version of umpire depending on blt starting from version 0.4.0

I don't know have knowledge about the required mappings between the versions of blt and umpire, so I look forward for comments from the maintainers.


Discussion:
Works for me. Thanks @albestro!

I think the version checks are fine as well. 5.0.0 doesn't build for me, but not because of the patch (because of this: https://github.com/LLNL/Umpire/blob/2196615500057e068f2d93597b4f8da89d582afb/CMakeLists.txt#L60-L64). That's fixed in 5.0.1. Would it be appropriate to simply remove 5.0.0 if 5.0.1 is strictly a non-breaking improvement over 5.0.0? That's not necessarily a decision for this PR though.
@davidbeckingsale 

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/chai/package.py b/var/spack/repos/builtin/packages/chai/package.py
--- a/var/spack/repos/builtin/packages/chai/package.py
+++ b/var/spack/repos/builtin/packages/chai/package.py
@@ -3,10 +3,12 @@
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
+import socket
+
 from spack import *
 
 
-class Chai(CMakePackage, CudaPackage, ROCmPackage):
+class Chai(CachedCMakePackage, CudaPackage, ROCmPackage):
     """
     Copy-hiding array interface for data migration between memory spaces
     """
@@ -85,60 +87,79 @@ class Chai(CMakePackage, CudaPackage, ROCmPackage):
 
     conflicts('+benchmarks', when='~tests')
 
-    def cmake_args(self):
+    def _get_sys_type(self, spec):
+        sys_type = spec.architecture
+        if "SYS_TYPE" in env:
+            sys_type = env["SYS_TYPE"]
+        return sys_type
+
+    @property
+    def cache_name(self):
+        hostname = socket.gethostname()
+        if "SYS_TYPE" in env:
+            hostname = hostname.rstrip('1234567890')
+        return "{0}-{1}-{2}@{3}.cmake".format(
+            hostname,
+            self._get_sys_type(self.spec),
+            self.spec.compiler.name,
+            self.spec.compiler.version
+        )
+
+    def initconfig_hardware_entries(self):
         spec = self.spec
+        entries = super(Chai, self).initconfig_hardware_entries()
 
-        options = []
-        options.append('-DBLT_SOURCE_DIR={0}'.format(spec['blt'].prefix))
-
-        options.append(self.define_from_variant('ENABLE_OPENMP', 'openmp'))
+        entries.append(cmake_cache_option("ENABLE_OPENMP", '+openmp' in spec))
 
         if '+cuda' in spec:
-            options.extend([
-                '-DENABLE_CUDA=ON',
-                '-DCMAKE_CUDA_SEPARABLE_COMPILATION=On',
-                '-DCUDA_SEPARABLE_COMPILATION=On',
-                '-DCUDA_TOOLKIT_ROOT_DIR=' + spec['cuda'].prefix])
+            entries.append(cmake_cache_option("ENABLE_CUDA", True))
+            entries.append(cmake_cache_option("CMAKE_CUDA_SEPARABLE_COMPILATION", True))
+            entries.append(cmake_cache_option("CUDA_SEPARABLE_COMPILATION", True))
 
             if not spec.satisfies('cuda_arch=none'):
                 cuda_arch = spec.variants['cuda_arch'].value
-                options.append('-DCUDA_ARCH=sm_{0}'.format(cuda_arch[0]))
+                entries.append(cmake_cache_string(
+                    "CUDA_ARCH", 'sm_{0}'.format(cuda_arch[0])))
+                entries.append(cmake_cache_string(
+                    "CMAKE_CUDA_ARCHITECTURES", '{0}'.format(cuda_arch[0])))
                 flag = '-arch sm_{0}'.format(cuda_arch[0])
-                options.append('-DCMAKE_CUDA_FLAGS:STRING={0}'.format(flag))
+                entries.append(cmake_cache_string(
+                    "CMAKE_CUDA_FLAGS", '{0}'.format(flag)))
         else:
-            options.append('-DENABLE_CUDA=OFF')
+            entries.append(cmake_cache_option("ENABLE_CUDA", False))
 
         if '+rocm' in spec:
-            options.extend([
-                '-DENABLE_HIP=ON',
-                '-DHIP_ROOT_DIR={0}'.format(spec['hip'].prefix)
-            ])
+            entries.append(cmake_cache_option("ENABLE_HIP", True))
+            entries.append(cmake_cache_path(
+                "HIP_ROOT_DIR", '{0}'.format(spec['hip'].prefix)))
             archs = self.spec.variants['amdgpu_target'].value
             if archs != 'none':
                 arch_str = ",".join(archs)
-                options.append(
-                    '-DHIP_HIPCC_FLAGS=--amdgpu-target={0}'.format(arch_str)
-                )
+                entries.append(cmake_cache_string(
+                    "HIP_HIPCC_FLAGS", '--amdgpu-target={0}'.format(arch_str)))
         else:
-            options.append('-DENABLE_HIP=OFF')
-
-        if '+raja' in spec:
-            options.extend(['-DENABLE_RAJA_PLUGIN=ON',
-                            '-DRAJA_DIR=' + spec['raja'].prefix])
-
-        options.append(self.define_from_variant('ENABLE_PICK', 'enable_pick'))
+            entries.append(cmake_cache_option("ENABLE_HIP", False))
 
-        options.append('-Dumpire_DIR:PATH='
-                       + spec['umpire'].prefix.share.umpire.cmake)
+        return entries
 
-        options.append('-DENABLE_TESTS={0}'.format(
-            'ON' if '+tests' in spec  else 'OFF'))
-
-        options.append(self.define_from_variant('ENABLE_BENCHMARKS', 'benchmarks'))
-
-        options.append(self.define_from_variant('ENABLE_EXAMPLES', 'examples'))
+    def initconfig_package_entries(self):
+        spec = self.spec
+        entries = []
 
-        options.append('-DENABLE_BENCHMARKS={0}'.format(
-            'ON' if '+benchmarks' in spec else 'OFF'))
+        entries.append(cmake_cache_path("BLT_SOURCE_DIR", spec['blt'].prefix))
+        if '+raja' in spec:
+            entries.append(cmake_cache_option("ENABLE_RAJA_PLUGIN", True))
+            entries.append(cmake_cache_path("RAJA_DIR", spec['raja'].prefix))
+        entries.append(cmake_cache_option('ENABLE_PICK', '+enable_pick' in spec))
+        entries.append(cmake_cache_path(
+            "umpire_DIR", spec['umpire'].prefix.share.umpire.cmake))
+        entries.append(cmake_cache_option("ENABLE_TESTS", '+tests' in spec))
+        entries.append(cmake_cache_option("ENABLE_BENCHMARKS", '+benchmarks' in spec))
+        entries.append(cmake_cache_option("ENABLE_EXAMPLES", '+examples' in spec))
+        entries.append(cmake_cache_option("BUILD_SHARED_LIBS", '+shared' in spec))
+
+        return entries
 
+    def cmake_args(self):
+        options = []
         return options
diff --git a/var/spack/repos/builtin/packages/raja/package.py b/var/spack/repos/builtin/packages/raja/package.py
--- a/var/spack/repos/builtin/packages/raja/package.py
+++ b/var/spack/repos/builtin/packages/raja/package.py
@@ -3,10 +3,12 @@
 #
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
+import socket
+
 from spack import *
 
 
-class Raja(CMakePackage, CudaPackage, ROCmPackage):
+class Raja(CachedCMakePackage, CudaPackage, ROCmPackage):
     """RAJA Parallel Framework."""
 
     homepage = "https://software.llnl.gov/RAJA/"
@@ -34,6 +36,11 @@ class Raja(CMakePackage, CudaPackage, ROCmPackage):
     version('0.4.1', tag='v0.4.1', submodules="True")
     version('0.4.0', tag='v0.4.0', submodules="True")
 
+    # export targets when building pre-2.4.0 release with BLT 0.4.0+
+    patch('https://github.com/LLNL/RAJA/commit/eca1124ee4af380d6613adc6012c307d1fd4176b.patch',
+          sha256='57dd531a50ac791b4bb214d34a4bf3fca1349354927c72915b7ccd20524701a9',
+          when='@:0.13.0 ^blt@0.4:')
+
     variant('openmp', default=True, description='Build OpenMP backend')
     variant('shared', default=True, description='Build Shared Libs')
     variant('examples', default=True, description='Build examples.')
@@ -63,54 +70,78 @@ class Raja(CMakePackage, CudaPackage, ROCmPackage):
             depends_on('camp +cuda cuda_arch={0}'.format(sm_),
                        when='cuda_arch={0}'.format(sm_))
 
-    def cmake_args(self):
-        spec = self.spec
-
-        options = []
+    def _get_sys_type(self, spec):
+        sys_type = spec.architecture
+        if "SYS_TYPE" in env:
+            sys_type = env["SYS_TYPE"]
+        return sys_type
 
-        options.append('-DBLT_SOURCE_DIR={0}'.format(spec['blt'].prefix))
+    @property
+    def cache_name(self):
+        hostname = socket.gethostname()
+        if "SYS_TYPE" in env:
+            hostname = hostname.rstrip('1234567890')
+        return "{0}-{1}-{2}@{3}.cmake".format(
+            hostname,
+            self._get_sys_type(self.spec),
+            self.spec.compiler.name,
+            self.spec.compiler.version
+        )
+
+    def initconfig_hardware_entries(self):
+        spec = self.spec
+        entries = super(Raja, self).initconfig_hardware_entries()
 
-        options.append(self.define_from_variant('ENABLE_OPENMP', 'openmp'))
+        entries.append(cmake_cache_option("ENABLE_OPENMP", '+openmp' in spec))
 
         if '+cuda' in spec:
-            options.extend([
-                '-DENABLE_CUDA=ON',
-                '-DCUDA_TOOLKIT_ROOT_DIR=%s' % (spec['cuda'].prefix)])
+            entries.append(cmake_cache_option("ENABLE_CUDA", True))
 
             if not spec.satisfies('cuda_arch=none'):
                 cuda_arch = spec.variants['cuda_arch'].value
-                options.append('-DCUDA_ARCH=sm_{0}'.format(cuda_arch[0]))
-                options.append('-DCMAKE_CUDA_ARCHITECTURES={0}'.format(cuda_arch[0]))
+                entries.append(cmake_cache_string(
+                    "CUDA_ARCH", 'sm_{0}'.format(cuda_arch[0])))
+                entries.append(cmake_cache_string(
+                    "CMAKE_CUDA_ARCHITECTURES", '{0}'.format(cuda_arch[0])))
         else:
-            options.append('-DENABLE_CUDA=OFF')
+            entries.append(cmake_cache_option("ENABLE_CUDA", False))
 
         if '+rocm' in spec:
-            options.extend([
-                '-DENABLE_HIP=ON',
-                '-DHIP_ROOT_DIR={0}'.format(spec['hip'].prefix)])
+            entries.append(cmake_cache_option("ENABLE_HIP", True))
+            entries.append(cmake_cache_path(
+                "HIP_ROOT_DIR", '{0}'.format(spec['hip'].prefix)))
             archs = self.spec.variants['amdgpu_target'].value
             if archs != 'none':
                 arch_str = ",".join(archs)
-                options.append(
-                    '-DHIP_HIPCC_FLAGS=--amdgpu-target={0}'.format(arch_str)
-                )
+                entries.append(cmake_cache_string(
+                    "HIP_HIPCC_FLAGS", '--amdgpu-target={0}'.format(arch_str)))
         else:
-            options.append('-DENABLE_HIP=OFF')
+            entries.append(cmake_cache_option("ENABLE_HIP", False))
 
-        options.append(self.define_from_variant('BUILD_SHARED_LIBS', 'shared'))
+        return entries
 
-        options.append(self.define_from_variant('ENABLE_EXAMPLES', 'examples'))
+    def initconfig_package_entries(self):
+        spec = self.spec
+        entries = []
 
-        options.append(self.define_from_variant('ENABLE_EXERCISES', 'exercises'))
+        entries.append(cmake_cache_path("BLT_SOURCE_DIR", spec['blt'].prefix))
+        entries.append(cmake_cache_path("camp_DIR", spec['camp'].prefix))
+        entries.append(cmake_cache_option("BUILD_SHARED_LIBS", '+shared' in spec))
+        entries.append(cmake_cache_option("ENABLE_EXAMPLES", '+examples' in spec))
+        entries.append(cmake_cache_option("ENABLE_EXERCISES", '+exercises' in spec))
 
         # Work around spack adding -march=ppc64le to SPACK_TARGET_ARGS which
         # is used by the spack compiler wrapper.  This can go away when BLT
         # removes -Werror from GTest flags
         if self.spec.satisfies('%clang target=ppc64le:') or not self.run_tests:
-            options.append('-DENABLE_TESTS=OFF')
+            entries.append(cmake_cache_option("ENABLE_TESTS", False))
         else:
-            options.append(self.define_from_variant('ENABLE_TESTS', 'tests'))
+            entries.append(cmake_cache_option("ENABLE_TESTS", True))
 
+        return entries
+
+    def cmake_args(self):
+        options = []
         return options
 
     @property
diff --git a/var/spack/repos/builtin/packages/umpire/package.py b/var/spack/repos/builtin/packages/umpire/package.py
--- a/var/spack/repos/builtin/packages/umpire/package.py
+++ b/var/spack/repos/builtin/packages/umpire/package.py
@@ -4,13 +4,14 @@
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
 import os
+import socket
 
 import llnl.util.tty as tty
 
 from spack import *
 
 
-class Umpire(CMakePackage, CudaPackage, ROCmPackage):
+class Umpire(CachedCMakePackage, CudaPackage, ROCmPackage):
     """An application-focused API for memory management on NUMA & GPU
     architectures"""
 
@@ -53,6 +54,11 @@ class Umpire(CMakePackage, CudaPackage, ROCmPackage):
     patch('cmake_version_check.patch', when='@4.1')
     patch('missing_header_for_numeric_limits.patch', when='@4.1:5.0.1')
 
+    # export targets when building pre-6.0.0 release with BLT 0.4.0+
+    patch('https://github.com/LLNL/Umpire/commit/5773ce9af88952c8d23f9bcdcb2e503ceda40763.patch',
+          sha256='f5c691752e4833a936bce224bbe0fe884d3afa84c5e5a4a481f59a12840159c9',
+          when='@:5.0.1 ^blt@0.4:')
+
     variant('fortran', default=False, description='Build C/Fortran API')
     variant('c', default=True, description='Build C API')
     variant('numa', default=False, description='Enable NUMA support')
@@ -95,69 +101,97 @@ class Umpire(CMakePackage, CudaPackage, ROCmPackage):
     # currently only available for cuda.
     conflicts('+shared', when='+cuda')
 
-    def cmake_args(self):
+    def _get_sys_type(self, spec):
+        sys_type = spec.architecture
+        if "SYS_TYPE" in env:
+            sys_type = env["SYS_TYPE"]
+        return sys_type
+
+    @property
+    def cache_name(self):
+        hostname = socket.gethostname()
+        if "SYS_TYPE" in env:
+            hostname = hostname.rstrip('1234567890')
+        return "{0}-{1}-{2}@{3}.cmake".format(
+            hostname,
+            self._get_sys_type(self.spec),
+            self.spec.compiler.name,
+            self.spec.compiler.version
+        )
+
+    def initconfig_compiler_entries(self):
         spec = self.spec
+        entries = super(Umpire, self).initconfig_compiler_entries()
 
-        options = []
-        options.append("-DBLT_SOURCE_DIR={0}".format(spec['blt'].prefix))
-        if spec.satisfies('@5.0.0:'):
-            options.append("-Dcamp_DIR={0}".format(spec['camp'].prefix))
+        if '+fortran' in spec and self.compiler.fc is not None:
+            entries.append(cmake_cache_option("ENABLE_FORTRAN", True))
+        else:
+            entries.append(cmake_cache_option("ENABLE_FORTRAN", False))
+
+        entries.append(cmake_cache_option("ENABLE_C", '+c' in spec))
+
+        return entries
+
+    def initconfig_hardware_entries(self):
+        spec = self.spec
+        entries = super(Umpire, self).initconfig_hardware_entries()
 
         if '+cuda' in spec:
-            options.extend([
-                '-DENABLE_CUDA=On',
-                '-DCUDA_TOOLKIT_ROOT_DIR=%s' % (spec['cuda'].prefix)])
+            entries.append(cmake_cache_option("ENABLE_CUDA", True))
 
             if not spec.satisfies('cuda_arch=none'):
                 cuda_arch = spec.variants['cuda_arch'].value
-                options.append('-DCUDA_ARCH=sm_{0}'.format(cuda_arch[0]))
-                options.append('-DCMAKE_CUDA_ARCHITECTURES={0}'.format(cuda_arch[0]))
+                entries.append(cmake_cache_string(
+                    "CUDA_ARCH", 'sm_{0}'.format(cuda_arch[0])))
+                entries.append(cmake_cache_string(
+                    "CMAKE_CUDA_ARCHITECTURES", '{0}'.format(cuda_arch[0])))
                 flag = '-arch sm_{0}'.format(cuda_arch[0])
-                options.append('-DCMAKE_CUDA_FLAGS:STRING={0}'.format(flag))
+                entries.append(cmake_cache_string(
+                    "CMAKE_CUDA_FLAGS", '{0}'.format(flag)))
 
-            if '+deviceconst' in spec:
-                options.append('-DENABLE_DEVICE_CONST=On')
+            entries.append(cmake_cache_option(
+                "ENABLE_DEVICE_CONST", spec.satisfies('+deviceconst')))
         else:
-            options.append('-DENABLE_CUDA=Off')
+            entries.append(cmake_cache_option("ENABLE_CUDA", False))
 
         if '+rocm' in spec:
-            options.extend([
-                '-DENABLE_HIP=ON',
-                '-DHIP_ROOT_DIR={0}'.format(spec['hip'].prefix)
-            ])
+            entries.append(cmake_cache_option("ENABLE_HIP", True))
+            entries.append(cmake_cache_path(
+                "HIP_ROOT_DIR", '{0}'.format(spec['hip'].prefix)))
             archs = self.spec.variants['amdgpu_target'].value
             if archs != 'none':
                 arch_str = ",".join(archs)
-                options.append(
-                    '-DHIP_HIPCC_FLAGS=--amdgpu-target={0}'.format(arch_str)
-                )
+                entries.append(cmake_cache_string(
+                    "HIP_HIPCC_FLAGS", '--amdgpu-target={0}'.format(arch_str)))
         else:
-            options.append('-DENABLE_HIP=OFF')
-
-        options.append('-DENABLE_C={0}'.format(
-            'On' if '+c' in spec else 'Off'))
-
-        options.append('-DENABLE_FORTRAN={0}'.format(
-            'On' if '+fortran' in spec else 'Off'))
-
-        options.append('-DENABLE_NUMA={0}'.format(
-            'On' if '+numa' in spec else 'Off'))
+            entries.append(cmake_cache_option("ENABLE_HIP", False))
 
-        options.append('-DENABLE_OPENMP={0}'.format(
-            'On' if '+openmp' in spec else 'Off'))
+        return entries
 
-        options.append('-DBUILD_SHARED_LIBS={0}'.format(
-            'On' if '+shared' in spec else 'Off'))
+    def initconfig_package_entries(self):
+        spec = self.spec
+        entries = []
 
-        options.append('-DENABLE_BENCHMARKS={0}'.format(
-            'On' if 'tests=benchmarks' in spec else 'Off'))
+        # TPL locations
+        entries.append("#------------------{0}".format("-" * 60))
+        entries.append("# TPLs")
+        entries.append("#------------------{0}\n".format("-" * 60))
 
-        options.append('-DENABLE_EXAMPLES={0}'.format(
-            'On' if '+examples' in spec else 'Off'))
+        entries.append(cmake_cache_path("BLT_SOURCE_DIR", spec['blt'].prefix))
+        if spec.satisfies('@5.0.0:'):
+            entries.append(cmake_cache_path("camp_DIR", spec['camp'].prefix))
+        entries.append(cmake_cache_option("ENABLE_NUMA", '+numa' in spec))
+        entries.append(cmake_cache_option("ENABLE_OPENMP", '+openmp' in spec))
+        entries.append(cmake_cache_option(
+            "ENABLE_BENCHMARKS", 'tests=benchmarks' in spec))
+        entries.append(cmake_cache_option("ENABLE_EXAMPLES", '+examples' in spec))
+        entries.append(cmake_cache_option("BUILD_SHARED_LIBS", '+shared' in spec))
+        entries.append(cmake_cache_option("ENABLE_TESTS", 'tests=none' not in spec))
 
-        options.append('-DENABLE_TESTS={0}'.format(
-            'Off' if 'tests=none' in spec else 'On'))
+        return entries
 
+    def cmake_args(self):
+        options = []
         return options
 
     def test(self):


Test patch: