Repository: spack/spack

Issue:
cuda: how to add non x86_64 versions without breaking the whole world
While trying to install `cuda@10.2.89` on `ppc64le` (not yet in spack, installer for the current `10.1` [is broken](https://devtalk.nvidia.com/default/topic/1063995/cuda-setup-and-installation/cuda-10-1-243-10-1-update-2-ppc64le-run-file-installation-issue/)), I stumbled upon the same issue we faced when dealing with another binary package in #11198: the current `cuda` package is implicitly `linux-x86_64` only, and trying to install it on other OS/arch ends up in really obscure errors. While waiting for #11206 to be worked on, I'm here to ask what would be the most acceptable way to add `cuda` installers for `ppc64le` given the tools spack currently provides.

Adopting the same pattern we used in #11198 would end up in something like this:

```python
version('10.2.89-ppc64le',
        sha256='5227774fcb8b10bd2d8714f0a716a75d7a2df240a9f2a49beb76710b1c0fc619',
        expand=False,
        url='http://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_440.33.01_linux_ppc64le.run')
```

What do you think about a provisional solution like this? Do you folks foresee some possible issue that would break everyone's `cuda` stuff (e.g.: as there is no way to constrain `x86_64` versions to `x86_64` platforms only, the reverse is also true)?




Discussion:
I would be fine with a change like this. See the `cudnn` package for another example where we do this. This is a perfectly fine workaround until we get better support for `when` parameters in the `version` directive. We should do the same thing for `jdk` so we can finally install Java on macOS.
Are the version numbers the same across platforms?  That is, is there
a 10.2.89 for x86_64 and 10.2.89 for ppc64le, etc?

Look at what I did for the hpcviewer package.  There, we have binary
files for x86_64, ppc64 and ppc64le.

One thing is: the `version()`, `variant()`, etc commands are all
executable.  So, you can use the python `platform.machine()` command
to decide what `version()` commands you want to run.

The point is that pure python code is outside of and before the spack
concretization, so it's legal to do this.

I would prefer something like that and not have the platform mixed in
with the name.

Btw, if the URLs have enough of a pattern, you can use
`url_for_version()` to compute them, which may be easier that writing
out the full URL every time.
Thanks for the hints folks. @mwkrentel while looking at your `hpcviewer` TIL that:

> *Class definitions are executable statements.* 
> From [here](https://docs.python.org/3.5/reference/compound_stmts.html#class-definitions).

Never underestimate Python's design (unwanted) side effects I guess :)

@adamjstewart what do you think about something like `hpcviewer`? Do you think it could be valuable over the straightforward workaround we were considering?

Edit:

> Are the version numbers the same across platforms? That is, is there
a 10.2.89 for x86_64 and 10.2.89 for ppc64le, etc?

Yes, luckily for us their versioning schema is sane.
IMHO, yes, yes, yes!

AFAIK, the python `platform.machine()` is the closest workaround you
can do now until the spack `when()` is implemented.  Plus, it has the
advantage that the interface (version names) won't change when a
better solution comes along.

If you put `ppc64le` in the version name, then you force packages that
depend on cuda to do awkward things like:

```
depends_on('cuda@10.2.89' + platform.machine())
```
Yes, the `hpcviewer` example looks great! Feel free to update other packages like `cudnn` to use this syntax. There are some cases where we would need more detail than is available in `platform.machine`, but this is fine for these simple packages.
@mwkrentel @adamjstewart In general, if you use `platform` you can't install for other targets than host - so I wouldn't consider it a good practice. If you are on an `x86_64` machine:
```console
spack install target=power8 <spec>%<cross-compiler>
```
will still install software for `x86_64` despite the request for `power8`. This will prevent the use of the recipe where e.g. the front-end has a different architecture than the back-end. Or am I missing some trivial workaround?

> In general, if you use platform you can't install for other targets
> than host - so I wouldn't consider it a good practice.

@alalazo Ok, fair enough.  The `platform.machine` solution won't work
for a cross compile.

So, what does work?  Fundamentally, you need to compute the url and
sha sum from the spack `target`.  Does spack have the machinery for
this?

AFAIK, the spack `target` is determined at concretization time and the
package class with `version` has to happen before that.  It's tempting
to want to make the target a `variant`, but you run into the same
timing problem.

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/cuda/package.py b/var/spack/repos/builtin/packages/cuda/package.py
--- a/var/spack/repos/builtin/packages/cuda/package.py
+++ b/var/spack/repos/builtin/packages/cuda/package.py
@@ -7,6 +7,42 @@
 from glob import glob
 from llnl.util.filesystem import LibraryList
 import os
+import platform
+
+# FIXME Remove hack for polymorphic versions
+# This package uses a ugly hack to be able to dispatch, given the same
+# version, to different binary packages based on the platform that is
+# running spack. See #13827 for context.
+# If you need to add a new version, please be aware that:
+#  - versions in the following dict are automatically added to the package
+#  - version tuple must be in the form (checksum, url)
+#  - checksum must be sha256
+#  - package key must be in the form '{os}-{arch}' where 'os' is in the
+#    format returned by platform.system() and 'arch' by platform.machine()
+
+_versions = {
+    '10.2.89': {
+        'Linux-x86_64': ('560d07fdcf4a46717f2242948cd4f92c5f9b6fc7eae10dd996614da913d5ca11', 'http://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_440.33.01_linux.run'),
+        'Linux-ppc64le': ('5227774fcb8b10bd2d8714f0a716a75d7a2df240a9f2a49beb76710b1c0fc619', 'http://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_440.33.01_linux_ppc64le.run')},
+    '10.1.243': {
+        'Linux-x86_64': ('e7c22dc21278eb1b82f34a60ad7640b41ad3943d929bebda3008b72536855d31', 'https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_418.87.00_linux.run')},
+    '10.0.130': {
+        'Linux-x86_64': ('92351f0e4346694d0fcb4ea1539856c9eb82060c25654463bfd8574ec35ee39a', 'https://developer.nvidia.com/compute/cuda/10.0/Prod/local_installers/cuda_10.0.130_410.48_linux')},
+    '9.2.88': {
+        'Linux-x86_64': ('8d02cc2a82f35b456d447df463148ac4cc823891be8820948109ad6186f2667c', 'https://developer.nvidia.com/compute/cuda/9.2/Prod/local_installers/cuda_9.2.88_396.26_linux')},
+    '9.1.85': {
+        'Linux-x86_64': ('8496c72b16fee61889f9281449b5d633d0b358b46579175c275d85c9205fe953', 'https://developer.nvidia.com/compute/cuda/9.1/Prod/local_installers/cuda_9.1.85_387.26_linux')},
+    '9.0.176': {
+        'Linux-x86_64': ('96863423feaa50b5c1c5e1b9ec537ef7ba77576a3986652351ae43e66bcd080c', 'https://developer.nvidia.com/compute/cuda/9.0/Prod/local_installers/cuda_9.0.176_384.81_linux-run')},
+    '8.0.61': {
+        'Linux-x86_64': ('9ceca9c2397f841024e03410bfd6eabfd72b384256fbed1c1e4834b5b0ce9dc4', 'https://developer.nvidia.com/compute/cuda/8.0/Prod2/local_installers/cuda_8.0.61_375.26_linux-run')},
+    '8.0.44': {
+        'Linux-x86_64': ('64dc4ab867261a0d690735c46d7cc9fc60d989da0d69dc04d1714e409cacbdf0', 'https://developer.nvidia.com/compute/cuda/8.0/prod/local_installers/cuda_8.0.44_linux-run')},
+    '7.5.18': {
+        'Linux-x86_64': ('08411d536741075131a1858a68615b8b73c51988e616e83b835e4632eea75eec', 'http://developer.download.nvidia.com/compute/cuda/7.5/Prod/local_installers/cuda_7.5.18_linux.run')},
+    '6.5.14': {
+        'Linux-x86_64': ('f3e527f34f317314fe8fcd8c85f10560729069298c0f73105ba89225db69da48', 'http://developer.download.nvidia.com/compute/cuda/6_5/rel/installers/cuda_6.5.14_linux_64.run')},
+}
 
 
 class Cuda(Package):
@@ -20,30 +56,11 @@ class Cuda(Package):
 
     homepage = "https://developer.nvidia.com/cuda-zone"
 
-    version('10.2.89',
-            sha256='560d07fdcf4a46717f2242948cd4f92c5f9b6fc7eae10dd996614da913d5ca11',
-            expand=False,
-            url="http://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_440.33.01_linux.run")
-    version('10.1.243',
-            sha256='e7c22dc21278eb1b82f34a60ad7640b41ad3943d929bebda3008b72536855d31',
-            expand=False,
-            url="https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_418.87.00_linux.run")
-    version('10.0.130', sha256='92351f0e4346694d0fcb4ea1539856c9eb82060c25654463bfd8574ec35ee39a', expand=False,
-            url="https://developer.nvidia.com/compute/cuda/10.0/Prod/local_installers/cuda_10.0.130_410.48_linux")
-    version('9.2.88', sha256='8d02cc2a82f35b456d447df463148ac4cc823891be8820948109ad6186f2667c', expand=False,
-            url="https://developer.nvidia.com/compute/cuda/9.2/Prod/local_installers/cuda_9.2.88_396.26_linux")
-    version('9.1.85', sha256='8496c72b16fee61889f9281449b5d633d0b358b46579175c275d85c9205fe953', expand=False,
-            url="https://developer.nvidia.com/compute/cuda/9.1/Prod/local_installers/cuda_9.1.85_387.26_linux")
-    version('9.0.176', sha256='96863423feaa50b5c1c5e1b9ec537ef7ba77576a3986652351ae43e66bcd080c', expand=False,
-            url="https://developer.nvidia.com/compute/cuda/9.0/Prod/local_installers/cuda_9.0.176_384.81_linux-run")
-    version('8.0.61', sha256='9ceca9c2397f841024e03410bfd6eabfd72b384256fbed1c1e4834b5b0ce9dc4', expand=False,
-            url="https://developer.nvidia.com/compute/cuda/8.0/Prod2/local_installers/cuda_8.0.61_375.26_linux-run")
-    version('8.0.44', sha256='64dc4ab867261a0d690735c46d7cc9fc60d989da0d69dc04d1714e409cacbdf0', expand=False,
-            url="https://developer.nvidia.com/compute/cuda/8.0/prod/local_installers/cuda_8.0.44_linux-run")
-    version('7.5.18', sha256='08411d536741075131a1858a68615b8b73c51988e616e83b835e4632eea75eec', expand=False,
-            url="http://developer.download.nvidia.com/compute/cuda/7.5/Prod/local_installers/cuda_7.5.18_linux.run")
-    version('6.5.14', sha256='f3e527f34f317314fe8fcd8c85f10560729069298c0f73105ba89225db69da48', expand=False,
-            url="http://developer.download.nvidia.com/compute/cuda/6_5/rel/installers/cuda_6.5.14_linux_64.run")
+    for ver, packages in _versions.items():
+        key = "{0}-{1}".format(platform.system(), platform.machine())
+        pkg = packages.get(key)
+        if pkg:
+            version(ver, sha256=pkg[0], url=pkg[1], expand=False)
 
     # macOS Mojave drops NVIDIA graphics card support -- official NVIDIA
     # drivers do not exist for Mojave. See


Test patch: