Repository: spack/spack

Issue:
Installation issue: `py-numexpr ^openblas symbol_suffix=64_` (i.e. as reqd by `julia`)
### Steps to reproduce the issue

```console
$ docker run --rm -it spack/ubuntu-jammy:latest
# spack install py-numexpr ^openblas +ilp64 symbol_suffix=64_
```

### Error message

<details><summary>Error message</summary>

<pre>
==> Installing py-numexpr-2.8.3-hdvpq4dxtp4e4ycdx2zijotqsmw7zhpb
==> No binary for py-numexpr-2.8.3-hdvpq4dxtp4e4ycdx2zijotqsmw7zhpb found: installing from source
==> Fetching https://mirror.spack.io/_source-cache/archive/38/389ceefca74eff30ec3fd03fc4c3b7ab3df8f22d1f235117a392ce702ed208c0.tar.gz
==> No patches needed for py-numexpr
==> py-numexpr: Executing phase: 'install'
==> Error: ProcessError: Command exited with status 1:
    '/opt/spack/opt/spack/linux-ubuntu22.04-skylake/gcc-11.3.0/python-3.10.10-5z45r2e723vlezh4ybzq53jspy55bpyc/bin/python3.10' '-m' 'pip' '-vvv' '--no-input' '--no-cache-dir' '--disable-pip-version-check' 'install' '--no-deps' '--ignore-installed' '--no-build-isolation' '--no-warn-script-location' '--no-index' '--prefix=/opt/spack/opt/spack/linux-ubuntu22.04-skylake/gcc-11.3.0/py-numexpr-2.8.3-hdvpq4dxtp4e4ycdx2zijotqsmw7zhpb' '.'

1 error found in build log:
     71      ╰─> See above for output.
     72    
     73      note: This error originates from a subprocess, and is likely not a problem with pip.
     74      full command: /opt/spack/opt/spack/linux-ubuntu22.04-skylake/gcc-11.3.0/python-3.10.10-5z45r2e723vlezh4ybzq53jspy55bpyc/bin/python3.10 /opt/spack/opt/spack/linux-ubuntu22.04-skylake/gcc-11.3.0/py-pip-23.0-4zsse4hisbf3gd6hf
           rlj2kb63agiu4hx/lib/python3.10/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py prepare_metadata_for_build_wheel /tmp/tmphm5av7jl
     75      cwd: /tmp/root/spack-stage/spack-stage-py-numexpr-2.8.3-hdvpq4dxtp4e4ycdx2zijotqsmw7zhpb/spack-src
     76      Preparing metadata (pyproject.toml): finished with status 'error'
  >> 77    error: metadata-generation-failed
     78    
     79    × Encountered error while generating package metadata.
     80    ╰─> See above for output.
     81    
     82    note: This is an issue with the package mentioned above, not pip.
     83    hint: See above for details.

See build log for details:
  /tmp/root/spack-stage/spack-stage-py-numexpr-2.8.3-hdvpq4dxtp4e4ycdx2zijotqsmw7zhpb/spack-build-out.txt
</pre></details>


### Information on your system

* **Spack:** 0.20.0.dev0
* **Python:** 3.10.6
* **Platform:** linux-ubuntu22.04-skylake
* **Concretizer:** clingo


### Additional information

[spack-build-out.txt](https://github.com/spack/spack/files/11302014/spack-build-out.txt)
[spack-build-env.txt](https://github.com/spack/spack/files/11302015/spack-build-env.txt)

The `julia` package depends strictly on `depends_on("openblas +ilp64 symbol_suffix=64_")`. This adds a suffix `64_` to the openblas symbols and library. When installing `py-numpy` with this same openblas, the symbol suffix is ignored and we end up with unresolved symbols in the `py-numpy` shared libraries, which become apparent when a downstream package tries to use them. This prevents unified environments with both `julia` and `py-numpy`.

Symbol `cblas_sgemm`:
```console
# nm /opt/spack/opt/spack/linux-ubuntu22.04-skylake/gcc-11.3.0/openblas-0.3.21-t7h3yzavddntb54jgiiqmkqns4jiqgu6/lib/libopenblas64_.so | grep cblas_sgemm
000000000008bb70 T cblas_sgemm64_
# nm /opt/spack/opt/spack/linux-ubuntu22.04-skylake/gcc-11.3.0/py-numpy-1.24.2-b3ktywszgnhw2ncz2656rospyqiwv24q/lib/python3.10/site-packages/numpy/core/_multiarray_umath.cpython-310-x86_64-linux-gnu.so | grep cblas_sgemm
                 U cblas_sgemm
```

The relevant issue and PR in julia are https://github.com/JuliaLang/julia/issues/4923 and https://github.com/JuliaLang/julia/pull/8734.
In my opinion this points to two issues, possibly:
1. The original reason for the suffix appears to have been avoiding symbol and library conflicts when using a vendored `openblas`. It may not be necessary to impose this requirement on external `openblas` installation such as those in spack.
2. `py-numpy` picks up the correct library through `spec["blas"].libs` but it doesn't get the suffix that way. There is a way to get this info, https://github.com/numpy/numpy/blob/main/numpy/distutils/system_info.py#L569, but I don't seem to succeed.

`julia` maintainers: @giordano @glennpj @haampie @vchuravy
`py-numpy` maintainers: @adamjstewart @pradyunsg @rgommers

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have run `spack maintainers <name-of-the-package>` and **@mentioned** any maintainers
- [X] I have uploaded the build log and environment files
- [X] I have searched the issues of this repo and believe this is not a duplicate


Discussion:
I'm +1 on this change, `64_` is the preferred symbol suffix for OpenBLAS (and ILP64 libraries in general). 

> `py-numpy` picks up the correct library through `spec["blas"].libs` but it doesn't get the suffix that way. There is a way to get this info, https://github.com/numpy/numpy/blob/main/numpy/distutils/system_info.py#L569, but I don't seem to succeed.

Since recently, that suffix is written correctly into the OpenBLAS pkg-config file, but NumPy doesn't know how to use that, and the auto-detection isn't good enough it looks like. There is a way to tell NumPy the suffix, which is to write it into the `site.cfg` file: https://github.com/numpy/numpy/blob/988381eabf7456eba0017e6bfe7e1f5566d91b94/site.cfg.example#L149-L155

That `site.cfg` is written out by Spack here: https://github.com/spack/spack/blob/806f630c8f83d499e97eb201d63ad8c8f4659670/var/spack/repos/builtin/packages/py-numpy/package.py#L238

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/py-numpy/package.py b/var/spack/repos/builtin/packages/py-numpy/package.py
--- a/var/spack/repos/builtin/packages/py-numpy/package.py
+++ b/var/spack/repos/builtin/packages/py-numpy/package.py
@@ -269,6 +269,9 @@ def write_library_dirs(f, dirs):
                 f.write("libraries = {0}\n".format(lapackblas_lib_names))
                 write_library_dirs(f, lapackblas_lib_dirs)
                 f.write("include_dirs = {0}\n".format(lapackblas_header_dirs))
+                symbol_suffix = spec["openblas"].variants["symbol_suffix"].value
+                if symbol_suffix != "none":
+                    f.write("symbol_suffix = {0}\n".format(symbol_suffix))
 
             if "^libflame" in spec or "^amdlibflame" in spec:
                 f.write("[flame]\n")


Test patch: