Repository: scipy/scipy

Issue:
BUG: Import error with Cygwin (tested versions 1.9.0, 1.9.3, 1.10.0, 1.11.0)
### Describe your issue.

Build works fine except that I need to add -D_DEFAULT_SOURCE to the C and C++ build arguments in the meson.build file because otherwise math symbols such as M_PI can't be found on my platform.

But then when trying to import scipy.interpolate.UnivariateSpline, I get the error message below.

Not sure why it's looking for a pyx file when the build is already over. 

### Reproducing Code Example

```python
from scipy.interpolate import UnivariateSpline
```


### Error message

```shell
Traceback (most recent call last):
  File "/cygdrive/c/Users/Andi/Desktop/sharedBox/plotBody.py", line 19, in <module>
    from scipy.interpolate import UnivariateSpline
  File "/usr/local/lib/python3.9/site-packages/scipy/interpolate/__init__.py", line 167, in <module>
    from ._interpolate import *
  File "/usr/local/lib/python3.9/site-packages/scipy/interpolate/_interpolate.py", line 8, in <module>
    import scipy.special as spec
  File "/usr/local/lib/python3.9/site-packages/scipy/special/__init__.py", line 671, in <module>
    from . import _orthogonal
  File "/usr/local/lib/python3.9/site-packages/scipy/special/_orthogonal.py", line 81, in <module>
    from scipy import linalg
  File "/usr/local/lib/python3.9/site-packages/scipy/__init__.py", line 200, in __getattr__
    return _importlib.import_module(f'scipy.{name}')
  File "/usr/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/usr/local/lib/python3.9/site-packages/scipy/linalg/__init__.py", line 209, in <module>
    from ._matfuncs import *
  File "/usr/local/lib/python3.9/site-packages/scipy/linalg/_matfuncs.py", line 20, in <module>
    from ._matfuncs_expm import pick_pade_structure, pade_UV_calc
  File "scipy/linalg/_matfuncs_expm.pyx", line 1, in init scipy.linalg._matfuncs_expm
ImportError: No such process
```


### SciPy/NumPy/Python version information

blas_mkl_info:   NOT AVAILABLE blis_info:   NOT AVAILABLE openblas_info:   NOT AVAILABLE atlas_3_10_blas_threads_info:   NOT AVAILABLE atlas_3_10_blas_info:   NOT AVAILABLE atlas_blas_threads_info:   NOT AVAILABLE atlas_blas_info:   NOT AVAILABLE accelerate_info:   NOT AVAILABLE blas_info:     libraries = ['blas', 'blas']     library_dirs = ['/usr/lib']     include_dirs = ['/usr/include']     language = c     define_macros = [('HAVE_CBLAS', None)] blas_opt_info:     define_macros = [('NO_ATLAS_INFO', 1), ('HAVE_CBLAS', None)]     libraries = ['blas', 'blas']     library_dirs = ['/usr/lib']     include_dirs = ['/usr/include']     language = c lapack_mkl_info:   NOT AVAILABLE openblas_lapack_info:   NOT AVAILABLE openblas_clapack_info:   NOT AVAILABLE flame_info:   NOT AVAILABLE atlas_3_10_threads_info:   NOT AVAILABLE atlas_3_10_info:   NOT AVAILABLE atlas_threads_info:   NOT AVAILABLE atlas_info:   NOT AVAILABLE lapack_info:     libraries = ['lapack', 'lapack']     library_dirs = ['/usr/lib']     language = f77 lapack_opt_info:     libraries = ['lapack', 'lapack', 'blas', 'blas']     library_dirs = ['/usr/lib']     language = c     define_macros = [('NO_ATLAS_INFO', 1), ('HAVE_CBLAS', None)]     include_dirs = ['/usr/include']


Discussion:
> Not sure why it's looking for a `.pyx` file when the build is already over

For ease of debugging, since you will usually edit the `.pyx` file to fix problems rather than the `.dll` file.  I've run into a few causes for `ImportError` on compiled extension modules: what happens when you run `cygcheck` on the file corresponding to `scipy.linalg._matfuncs_expm`?

EDIT: In DWesl/scipy#1 (1.8 era), I used `_XOPEN_SOURCE=600` rather than `_DEFAULT_SOURCE`; we'll see if that works now when DWesl/scipy#4 finishes compiling in a couple of hours.
Output of cygcheck is 

```
cygcheck ./_matfuncs_expm.cpython-39-x86_64-cygwin.dll
C:\cygwin64\usr\local\lib\python3.9\site-packages\scipy\linalg\_matfuncs_expm.cpython-39-x86_64-cygwin.dll
  C:\cygwin64\bin\cygwin1.dll
    C:\Windows\system32\KERNEL32.dll
      C:\Windows\system32\ntdll.dll
      C:\Windows\system32\KERNELBASE.dll
  C:\cygwin64\bin\libpython3.9.dll
    C:\cygwin64\bin\cygintl-8.dll
      C:\cygwin64\bin\cygiconv-2.dll
    C:\cygwin64\bin\cyggcc_s-seh-1.dll
```

I just tried compiling it with _XOPEN_SOURCE=600. Same ImportError.
Well there goes that idea.  

The error message reminds me most of attempts to open the Tk DLL using Linux functions on Cygwin ([Pillow's ImageTk did this](https://github.com/friedrichromstedt/bughunting-02#description-of-the-bug-and-of-the-expected-behaviour)), but there doesn't seem to be anything of the sort in the source file, especially at top level where it would produce an `ImportError`.

Do other Cython packages on your system import cleanly?  What about extensions compiled using meson?
Just compiled a (pretty much hello-world-ish) example with meson and Cython and it worked without giving the ImportError.

I made the observation that when I comment out _matfuncs_expm in _matfuncs.py, the next import error is:

```
File "scipy/linalg/_decomp_update.pyx", line 1, in init scipy.linalg._decomp_update
ImportError: No such process
```

Seems that both these pyx files are compiled from pyx.in files.. This is another preprocessing step, right?, producing pyx files from the pyx.in files before cythonizing the pyx files? Could the problem stem from this preprocessing step somehow?
My most recent build is telling me that it should be `_XOPEN_SOURCE=700` because that's what's in `pyconfig.h`, so the underlying problem might be that some files include standard headers before `pyconfig.h` sets the visibility macros that say which parts of the standard headers we want to see.  Which headers are you seeing problems in?

Almost certainly separate from the `ImportError`; I still have no idea what's causing that.  If you have the build tree, you should have corresponding `.pyx` files around somewhere you can look through.  My best guess is still "trying to get functions out of a DLL the wrong way", but I don't see anything obvious in either file.
Out of curiosity, how did you get a complete meson build on Cygwin?  When I try, [it doesn't bother linking `_flapack` against blas, so the link fails with undefined symbol references](https://github.com/DWesl/scipy/actions/runs/4093587262/jobs/7059128133#step:9:470). 
I created a file openblas.pc in /usr/pkgconfig with

```
libdir=/usr/lib
includedir=/usr/include

Name: OPENBLAS
Description: openblas
Version: 3.10.1
URL: http://www.netlib.org/lapack/
Libs: -L${libdir} -lblas -llapack
```

Didn't get any linker errors so it seems all the symbols are present in either libblas or liblapack. I have the default Cygwin liblapack-devel and libopenblas (as you probably know, there is no libopenblas-devel, but it seems like all the neccessary headers were around and found somehow).
Just created a simple file with only
```
from _matfuncs_expm import pick_pade_structure, pade_UV_calc
```
in the same directory as the _matfuncs_expm.cpython-39-x86_64-cygwin.dll.

The error I get is
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/scipy/linalg/test.py", line 1, in <module>
    from _matfuncs_expm import pick_pade_structure, pade_UV_calc
  File "scipy/linalg/_matfuncs_expm.pyx", line 1, in init scipy.linalg._matfuncs_expm
  File "/usr/local/lib/python3.9/site-packages/scipy/linalg/__init__.py", line 209, in <module>
    from ._matfuncs import *
  File "/usr/local/lib/python3.9/site-packages/scipy/linalg/_matfuncs.py", line 20, in <module>
    from ._matfuncs_expm import pick_pade_structure, pade_UV_calc
ImportError: cannot import name 'pick_pade_structure' from partially initialized module '_matfuncs_expm' (most likely due to a circular import) (/usr/local/lib/python3.9/site-packages/scipy/linalg/_matfuncs_expm.cpython-39-x86_64-cygwin.dll)
```
Also just fired up gdb to step through this import process (as far as debug symbols were available) starting from `PyInit__matfuncs_expm`

The result is here
[https://github.com/akandi/Scipy_1.10.0_BuildOnCygwin/blob/master/gdb_log.txt](https://github.com/akandi/Scipy_1.10.0_BuildOnCygwin/blob/master/gdb_log.txt)
> I created a file openblas.pc in /usr/pkgconfig with
> 
> ```
> libdir=/usr/lib
> includedir=/usr/include
> 
> Name: OPENBLAS
> Description: openblas
> Version: 3.10.1
> URL: http://www.netlib.org/lapack/
> Libs: -L${libdir} -lblas -llapack
> ```

I'd prefer `-llapack -lblas`, but your order might grab Lapack functions from OpenBLAS rather than the reference Lapack.

> Didn't get any linker errors so it seems all the symbols are present in either libblas or liblapack. I have the default Cygwin liblapack-devel and libopenblas (as you probably know, there is no libopenblas-devel, but it seems like all the neccessary headers were around and found somehow).

`liblapack-devel` provides headers for the standard Lapack and Blas routines, but some configury will check for OpenBLAS-specific functions (usually related to setting threading, I think).  I used [meson options to tell the build to use reference lapack and blas](https://scipy.github.io/devdocs/dev/contributor/meson_advanced.html), but that seems to be broken.  
Switching the link order fixes the problem for me. Seems like the root cause is pretty much what you expected and the Python error message isn't very helpful.
Let me know if I can do some more testing on my system.
And awesome that you responded so quickly. Thanks
Glad that yours is working.

I've got [a reproducer running into the same problem](https://github.com/DWesl/scipy/pull/4/checks?check_run_id=11148992889) at DWesl/scipy#4.  Do those changes fix the compile problem you ran into?  

[It looks like the problem is with or after `cimport cython_lapack`](https://github.com/DWesl/scipy/actions/runs/4116772515/jobs/7107234903#step:12:3166), which should be working fine.  Do you have any suggestions on how I can get the import working on the CI runner?
Looks to me a very similar if not the same problem that I had. Maybe the underlying cause really is some kind of circular import.
I just tested building scipy without my pkg-config hack and instead setting blas=blas and lapack=lapack in the meson config. For me the build failed at first due to some sub-libraries depending only on lapack and missing some symbols from blas. I edited all nested meson.build files that had lapack as a dependency to also include blas and for good measure switched the order from "black, lapack" to "lapack, blas" where applicable. The resulting scipy works for me. Maybe this is worth a try to run the build runner on such a patched version.
That worked, thank you.

Resolving patch:
diff --git a/dev.py b/dev.py
--- a/dev.py
+++ b/dev.py
@@ -436,6 +436,13 @@ def setup_build(cls, dirs, args):
                 raise RuntimeError("Can't build into non-empty directory "
                                    f"'{build_dir.absolute()}'")
 
+        if sys.platform == "cygwin":
+            # Cygwin only has netlib lapack, but can link against
+            # OpenBLAS rather than netlib blas at runtime.  There is
+            # no libopenblas-devel to enable linking against
+            # openblas-specific functions or OpenBLAS Lapack
+            cmd.extend(["-Dlapack=lapack", "-Dblas=blas"])
+
         build_options_file = (
             build_dir / "meson-info" / "intro-buildoptions.json")
         if build_options_file.exists():
@@ -547,6 +554,11 @@ def install_project(cls, dirs, args):
         with open(dirs.installed / ".gitignore", "w") as f:
             f.write("*")
 
+        if sys.platform == "cygwin":
+            rebase_cmd = ["/usr/bin/rebase", "--database", "--oblivious"]
+            rebase_cmd.extend(Path(dirs.installed).glob("**/*.dll"))
+            subprocess.check_call(rebase_cmd)
+
         print("Installation OK")
         return
 


Test patch: