Repository: spack/spack

Issue:
Fix neovim install on M1
With #29228 and #29229 in, the `spack install neovim` fails with:
```
     1022    undef: _tigetstr
     1023    undef: _cur_term
     1024    undef: _setupterm
  >> 1025    Undefined symbols for architecture arm64:
     1026      "_tigetstr", referenced from:
     1027          _try_load_terminfo_key in libtermkey.a(driver-ti.o)
     1028      "_cur_term", referenced from:
     1029          _load_terminfo in libtermkey.a(driver-ti.o)
     1030      "_setupterm", referenced from:
     1031          _new_driver in libtermkey.a(driver-ti.o)
     1032          _load_terminfo in libtermkey.a(driver-ti.o)
     1033    ld: symbol(s) not found for architecture arm64
```
While linking the `nvim` executable. These symbols seem to be coming from `ncurses`, but linking `ncurses` explicitly didn't seem to fix it. However, the current PR fixes it. One must turn off `termlib` in `ncurses` and then one must explicitly link it. Then `nvim` builds just fine. I am opening this PR as a Draft, because the `+termlib` seems hardwired in `lua`, so I don't know how to fix this properly. Also just adding `ncurses` in the cmake for `neovim` doesn't feel right, one should explicitly depend on `ncurses` and then find it using cmake. I don't have time to work on that. But this PR might be helpful to others to finish this work. Either way, neovim seems to work fine now.


Discussion:
Hi @certik! I noticed that the following package(s) don't yet have maintainers:

* libluv
* libtermkey
* lua
* ncurses

Are you interested in adopting any of these package(s)? If so, simply add the following to the package class:
```python
    maintainers = ['certik']
```
If not, could you contact the developers of this package and see if they are interested? You can quickly see who has worked on a package with `spack blame`:

```bash
$ spack blame libluv
```
Thank you for your help! Please don't add maintainers without their consent.

_You don't have to be a Spack expert or package developer in order to be a "maintainer," it just gives us a list of users willing to review PRs or debug issues relating to this package. A package can have multiple maintainers; just add a list of GitHub handles of anyone who wants to volunteer._

Thanks @certik for opening this draft PR!

> With #29228 and #29229 in, the `spack install neovim` fails with:
> 
> ```
>      1022    undef: _tigetstr
>      1023    undef: _cur_term
>      1024    undef: _setupterm
>   >> 1025    Undefined symbols for architecture arm64:
>      1026      "_tigetstr", referenced from:
>      1027          _try_load_terminfo_key in libtermkey.a(driver-ti.o)
>      1028      "_cur_term", referenced from:
>      1029          _load_terminfo in libtermkey.a(driver-ti.o)
>      1030      "_setupterm", referenced from:
>      1031          _new_driver in libtermkey.a(driver-ti.o)
>      1032          _load_terminfo in libtermkey.a(driver-ti.o)
>      1033    ld: symbol(s) not found for architecture arm64
> ```

I quickly started investigating and this stands out from my search https://github.com/neovim/neovim/issues/2484#issuecomment-413634748, where the problem looks similar.

> While linking the `nvim` executable. These symbols seem to be coming from `ncurses`, but linking `ncurses` explicitly didn't seem to fix it.

In particular, those symbols are not from `ncurses` (where are marked as "unknown", so they are external)

```
nm `spack location -i ncurses`/lib/libncurses.so | egrep "(tigetstr|cur_term|setupterm)"
                 U cur_term@@NCURSES6_TINFO_5.0.19991023
                 U _nc_setupterm@@NCURSES6_TINFO_5.5.20051010
                 U tigetstr@@NCURSES6_TINFO_5.0.19991023
```

but from `tinfo`

```
nm `spack location -i ncurses`/lib/libtinfo.so | egrep "(tigetstr|cur_term|setupterm)"
000000000003ede8 B cur_term
0000000000015d80 T _nc_setupterm
0000000000016370 T setupterm
0000000000017610 T tigetstr
0000000000017510 T tigetstr_sp
```

AFAICT, the error above should be related to a missing `-ltinfo` in the linking line (which should be enforced by pkg-config).

```
cat `spack location -i ncurses`/lib/pkgconfig/ncurses.pc
...
Name: ncurses
Description: ncurses 6.2 library
Version: ${version}
URL: https://invisible-island.net/ncurses
Requires.private:
Libs:  -lncurses -ltinfo
Libs.private:
Cflags:  -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -I${includedir} -I/home/ubuntu/spack/opt/spack/linux-ubuntu20.04-haswell/gcc-9.3.0/ncurses-6.2-75db2nqyv5sczfrvavpsag7ufa7ksunn/include
```

and it seems enforced.

> However, the current PR fixes it. One must turn off `termlib` in `ncurses` and then one must explicitly link it. Then `nvim` builds just fine. I am opening this PR as a Draft, because the `+termlib` seems hardwired in `lua`, so I don't know how to fix this properly.

I agree that turning off `~termlib` is just a workaround, if not fully sure I won't go for that. 

> Also just adding `ncurses` in the cmake for `neovim` doesn't feel right, one should explicitly depend on `ncurses` and then find it using cmake. I don't have time to work on that. But this PR might be helpful to others to finish this work. Either way, neovim seems to work fine now.

I fully agree, it does not look the right path to add `ncurses` like this. Actually it is a dependency, but looking at the template config file for pkg-config from libtermkey itself (https://github.com/neovim/libtermkey/blob/master/termkey.pc.sh) it seems not specified. Probably the problem is there, and since FindLibTermkey.cmake script is pkg-config based, it fails to correctly set the linking phase of neovim (which would consist in doing automatically what you suggest forcing via the patch).


Unfortunately I don't have the latest M1 system (but I speculate it may be to something different in the latest macOS version and/or your specific setup), and I was not able to replicate the problem.

I think the fundamental fix is the one you did in #29229, by adding the dependency to `pkgconfig` (which is the virtual provider for `pkg-config` too). Without that, I was getting an error building `libtermkey` (on a clean basic linux with a very minimal set of packages installed). After adding that I was able to build neovim both with `libtermkey@0.22` and `libtermkey@0.18`.

I will try on a macOS machine next week (it's not an M1, and it has BigSur on it), just to see if the scenario above is confirmed, or I'm able to replicate your problem.

Anyway, I will look about the pkg-config fix in libtermkey, but if I won’t be able to replicate your problem, I will ask you if you can give it a try (actually you can just test it by adding ncurses in the pkg-config file in the installation path of libtermkey)
> Actually it is a dependency, but looking at the template config file for pkg-config from libtermkey itself (https://github.com/neovim/libtermkey/blob/master/termkey.pc.sh) it seems not specified. Probably the problem is there, and since FindLibTermkey.cmake script is pkg-config based, it fails to correctly set the linking phase of neovim (which would consist in doing automatically what you suggest forcing via the patch).

From what I understand the problem is the one stated above. IMHO libtermkey pkg-config script lacks of the dependencies.

Unfortunately I'm not able to replicate the problem, so looking forward to your feedback @certik on #29238 

Thanks again
I will note that `ncurses+termlib` leaves the symbols such as `_tigetstr` undefined, but `ncurses~termlib` shows these symbols as defined. So that's why this PR worked for me.
I rebased on top of the latest `develop` branch. It still builds for me on M1.

Resolving patch:
diff --git a/var/spack/repos/builtin/packages/libtermkey/package.py b/var/spack/repos/builtin/packages/libtermkey/package.py
--- a/var/spack/repos/builtin/packages/libtermkey/package.py
+++ b/var/spack/repos/builtin/packages/libtermkey/package.py
@@ -19,7 +19,7 @@ class Libtermkey(Package):
     version('0.14', sha256='3d114d4509499b80a583ea39cd35f18268aacf4a7bbf56c142cd032632005c79')
 
     depends_on('libtool', type='build')
-    depends_on('ncurses')
+    depends_on('unibilium')
     depends_on('pkgconfig')
 
     def install(self, spec, prefix):


Test patch: