epics-base / p4p

Python bindings for the PVAccess network client and server.
BSD 3-Clause "New" or "Revised" License
27 stars 38 forks source link

pip install p4p fails on linux-aarch64 #156

Open ffeldbauer opened 2 months ago

ffeldbauer commented 2 months ago

Hey,

I currently try to set up a Docker image for linux/amd64, linux/arm64 and linux/arm/v7 containing p4p and some basic configurations for our setups.

Creating the image for linux/amd64 works fine, but I ran into issues with the two arm architectures. pip tries to compile epicscorelibs, pvxslibs and p4p and fails with

[...]
  File "/tmp/pip-build-env-aolqqkng/overlay/lib/python3.11/site-packages/numpy/distutils/misc_util.py", line 91, in get_num_build_jobs
    from numpy.distutils.core import get_distribution
  File "/tmp/pip-build-env-aolqqkng/overlay/lib/python3.11/site-packages/numpy/distutils/core.py", line 24, in <module>
    from numpy.distutils.command import config, config_compiler, \
  File "/tmp/pip-build-env-aolqqkng/overlay/lib/python3.11/site-packages/numpy/distutils/command/config.py", line 19, in <module>
    from numpy.distutils.mingw32ccompiler import generate_manifest
  File "/tmp/pip-build-env-aolqqkng/overlay/lib/python3.11/site-packages/numpy/distutils/mingw32ccompiler.py", line 27, in <module>
    from distutils.msvccompiler import get_build_version as get_build_msvc_version
ModuleNotFoundError: No module named 'distutils.msvccompiler'

But when I execute python -c "from distutils.msvccompiler import get_build_version as get_build_msvc_version" it works without an error.

Any idea what goes wrong here? Maybe I'm missing some build dependencies in my Dockerfile?

ffeldbauer commented 2 months ago

In case anyone is interessted: I grabbed a Raspberry Pi 4 and set up a fresh Raspbian OS 64-bit image and tried to install p4p with the same result. After some searching on the internet, I found that setuptools 60+ made some breaking changes to their own distutils.

So I cloned this repo (tag 4.1.12) and changed pyproject.toml:

-"setuptools",
+"setuptools<60",

Installing p4p still does not work, but it failed much later:

  src/pvxs_value.cpp:540:75: error: cannot convert ‘PyObject*’ {aka ‘_object*’} to ‘const PyArrayObject*’ {aka ‘const tagPyArrayObject_fields*’}
    540 |             memcpy(dest.data(), PyArray_DATA(arr.obj), PyArray_NBYTES(arr.obj));
        |                                                                       ~~~~^~~
        |                                                                           |
        |                                                                           PyObject* {aka _object*}

EDIT: Also changing the version of numpy and Cython solved the issue:

-"numpy",
-"Cython>=0.20",
+"numpy<2.0",
+"Cython>=0.20,<3.0",

With these two changes to pyproject.toml I was able to compile p4p 4.1.12 on linux-aarch64.

OCopping commented 2 months ago
  src/pvxs_value.cpp:540:75: error: cannot convert ‘PyObject*’ {aka ‘_object*’} to ‘const PyArrayObject*’ {aka ‘const tagPyArrayObject_fields*’}
    540 |             memcpy(dest.data(), PyArray_DATA(arr.obj), PyArray_NBYTES(arr.obj));
        |                                                                       ~~~~^~~
        |                                                                           |
        |                                                                           PyObject* {aka _object*}

This was a breaking change with Numpy 2.0, so that makes sense why pinning to Numpy<2.0 worked. I patched this in commit 1db6252, which corresponds to p4p 4.2.0a1.

What version of Python have you been building p4p against?

I am also surprised you get an error regarding MSVCCompiler as that is normally only used when compiling on Windows systems...

EDIT: I just noticed in your stacktrace is says "python3.11", which is currently broken with p4p 4.2.0a1. We are waiting for the next Numpy release, and once that is done I can merge #152 which should fix this.

ffeldbauer commented 2 months ago

For the docker container I used FROM python:3.11-slim (so it is python 3.11.x). On my raspberry pi, it is 3.11.2

I tried building p4p 4.1.12 (f5c96ba) to have the same version as I have on my amd64 machines where I'm using the prebuild binaries from pypi.org

ffeldbauer commented 2 months ago

btw: I just noticed that the pvagw does not work on amd64 after installing it via pip install p4p:

epics@6b6bf6a3e8b2:/config$ pvagw -h
Traceback (most recent call last):
  File "/usr/local/bin/pvagw", line 5, in <module>
    from p4p.gw import main
  File "/usr/local/lib/python3.11/site-packages/p4p/__init__.py", line 14, in <module>
    from .wrapper import Value, Type
  File "/usr/local/lib/python3.11/site-packages/p4p/wrapper.py", line 5, in <module>
    from . import _p4p
  File "src/p4p/_p4p.pyx", line 1, in init p4p._p4p
ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject

You have to manually downgrade numpy with

pip install -U "numpy<2.0"

Maybe it would be a good idea to add limitations to pyproject.toml, not only to use a certain minimum version but also limit the version upwards, as numpy 2.1.1 does not seem to be ABI compatible with numpy 1.26.4

At least in the current HEAD I still see, that numpy and setuptools have no restrictions at all

juanfem commented 2 months ago

Same issue here. I managed to install p4p on linux-aarch64 following @ffeldbauer instructions. Thanks!

mdavidsaver commented 2 months ago

152 is merged, and p4p==4.2.0a2 is available for testing.

mdavidsaver commented 2 months ago

I found that setuptools 60+ made some breaking changes to their own distutils.

@ffeldbauer Can you provide a reference link? Was this change intentional?

ffeldbauer commented 2 months ago

@mdavidsaver Yes, here are the two links that I found on my original issue:

Where I first found an answer to the original issue: https://github.com/spacepy/spacepy/issues/633#issuecomment-1216972674

And the Pull request where this change was acutally done: https://github.com/pypa/setuptools/pull/3505

EDIT: In the discussion of the pull request is even mentioned that this change broke numpy and implicit amongst others

ffeldbauer commented 2 months ago
  The conflict is caused by:
      The user requested epicscorelibs==7.0.7.99.0.2
      pvxslibs 1.3.1 depends on epicscorelibs<7.0.7.99.2 and >=7.0.7.99.1.1a2

Seems to me, there is a 0 to much in the requested version of epicscorelibs for 4.2.0a2

mdavidsaver commented 2 months ago

I am able to install into a fresh virtualenv

$ virtualenv p4pdev
...
$ ./p4pdev/bin/pip install p4p==4.2.0a2
...
$ ./p4pdev/bin/nose2 p4p
...
Ran 151 tests in 39.173s

OK (skipped=2)
$ ./p4pdev/bin/pip freeze
epicscorelibs==7.0.7.99.1.1a3
nose2==0.15.1
numpy==2.1.1
p4p==4.2.0a2
ply==3.11
pvxslibs==1.3.2a2
setuptools-dso==2.11

...

$ uname -r
6.10.6+bpo-amd64
$ python --version
Python 3.11.2
ffeldbauer commented 2 months ago

@mdavidsaver strange that it works for you.

Looking at the pyproject files: https://github.com/epics-base/p4p/blob/b1052f4886ccf65d5459f58293acb73afe1521fb/pyproject.toml#L9

For python 3.11 you require epicscorelibs to be exactly of version 7.0.7.99.0.2 and pvxs to be version 1.3.1. In pvxs 1.3.1 the version of epicscorelibs should be higher then 7.0.3.99.2.0a1:

https://github.com/epics-base/pvxs/blob/93ab81c1532b7078aa261d8608db713898b545b6/pyproject.toml#L2

$ uname -r
6.6.47+rpt-rpi-v8
$ python3 --version
Python 3.11.2
ffeldbauer commented 2 months ago

Ok...it seems to be (again) an issue with linux-aarch64:

On my desktop PC:

$ python3 -m venv p4ptest
$ ./p4ptest/bin/pip install p4p==4.2.0a2
Collecting p4p==4.2.0a2
  Downloading p4p-4.2.0a2-cp311-cp311-manylinux2014_x86_64.whl (427 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 427.5/427.5 kB 5.2 MB/s eta 0:00:00
Collecting epicscorelibs<7.0.7.99.2,>=7.0.7.99.1.1a3
  Downloading epicscorelibs-7.0.7.99.1.1a3-cp311-cp311-manylinux2014_x86_64.whl (5.4 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.4/5.4 MB 20.5 MB/s eta 0:00:00
Collecting pvxslibs<1.4.0a1,>=1.3.2a2
  Downloading pvxslibs-1.3.2a2-cp311-cp311-manylinux2014_x86_64.whl (2.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.6/2.6 MB 8.8 MB/s eta 0:00:00
Collecting nose2>=0.8.0
  Downloading nose2-0.15.1-py3-none-any.whl (211 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 211.3/211.3 kB 3.4 MB/s eta 0:00:00
Collecting ply
  Downloading ply-3.11-py2.py3-none-any.whl (49 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 49.6/49.6 kB 1.0 MB/s eta 0:00:00
Collecting numpy>=1.7
  Downloading numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.3 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 16.3/16.3 MB 23.9 MB/s eta 0:00:00
Requirement already satisfied: setuptools in ./p4ptest/lib/python3.11/site-packages (from epicscorelibs<7.0.7.99.2,>=7.0.7.99.1.1a3->p4p==4.2.0a2) (66.1.1)
Collecting setuptools-dso>=2.11a2
  Downloading setuptools_dso-2.11-py2.py3-none-any.whl (23 kB)
Installing collected packages: ply, setuptools-dso, numpy, nose2, epicscorelibs, pvxslibs, p4p
Successfully installed epicscorelibs-7.0.7.99.1.1a3 nose2-0.15.1 numpy-2.1.1 p4p-4.2.0a2 ply-3.11 pvxslibs-1.3.2a2 setuptools-dso-2.11

$ ./p4ptest/bin/pip freeze
epicscorelibs==7.0.7.99.1.1a3
nose2==0.15.1
numpy==2.1.1
p4p==4.2.0a2
ply==3.11
pvxslibs==1.3.2a2
setuptools-dso==2.11

$ python3 --version
Python 3.11.2
$ uname -r -m
6.1.0-25-amd64 x86_64
juanfem commented 2 months ago

I can install p4p-4.2.0a2 in aarch64, both on linux (Docker) and darwin, although tests fail when running on Docker (maybe that is expected):

Ran 151 tests in 44.167s

FAILED (failures=125, errors=11, skipped=2)

Errors are TimeoutError and failures are AssertionError: Leftovers from previous test: ClientContextImpl = 1

On macOS, all tests pass

Ran 151 tests in 46.063s

OK (skipped=2)

but I get funny error messages printed on the screen:

.pthread_attr_destroy  ERROR Invalid argument
free_threadInfoThread non-EPICS_13372059648 (0x600003e3a400) can't proceed, suspending.
Dumping a stack trace of thread 'non-EPICS_13372059648':
[       0x10633ac68]: /Users/juanfestebanmuller/temp/p4pdev/lib/python3.11/site-packages/epicscorelibs/lib/libCom.7.0.7.99.1.dylib(epicsStackTrace+0x74)
[       0x106351ed0]: /Users/juanfestebanmuller/temp/p4pdev/lib/python3.11/site-packages/epicscorelibs/lib/libCom.7.0.7.99.1.dylib(cantProceed+0x44)
[       0x10633e3b8]: /Users/juanfestebanmuller/temp/p4pdev/lib/python3.11/site-packages/epicscorelibs/lib/libCom.7.0.7.99.1.dylib(free_threadInfo+0x14c)
[       0x196a23870]: /usr/lib/system/libsystem_pthread.dylib(_pthread_tsd_cleanup+0x1e8)
[       0x196a26684]: /usr/lib/system/libsystem_pthread.dylib(_pthread_exit+0x54)
[       0x196a25fa0]: /usr/lib/system/libsystem_pthread.dylib(_pthread_start+0x94)
[       0x196a20d34]: /usr/lib/system/libsystem_pthread.dylib(thread_start+0x8)

-------------- config --------------

Docker:

# ./p4ptest/bin/pip freeze
epicscorelibs==7.0.7.99.1.1a3
nose2==0.15.1
numpy==2.1.1
p4p==4.2.0a2
ply==3.11
pvxslibs==1.3.2a2
setuptools-dso==2.11

# python --version
Python 3.11.9

# uname -r -m
6.6.41-0-virt x86_64

macOS

% ./p4pdev/bin/pip freeze
epicscorelibs==7.0.7.99.1.1a3
nose2==0.15.1
numpy==2.1.1
p4p==4.2.0a2
ply==3.11
pvxslibs==1.3.2a2
setuptools-dso==2.11

% python --version
Python 3.11.9

% uname -r -m
23.6.0 arm64
ffeldbauer commented 2 months ago

6.6.41-0-virt x86_64

Your docker container is x86_64. For this architecture I was also able to install p4p 4.2.0a2 too. But I need to operate p4p on a raspberry pi, so I need it for aarch64.

I tried to look at the various libs more closely:

$ python3 -m venv pvxslibs_test
$ ./pvxslibs_test/bin/pip install pvxslibs==1.3.1
[...]
$ ./pvxslibs_test/bin/python3 -c "import epicscorelibs.version; print( epicscorelibs.version.abi_requires() )"
epicscorelibs >=7.0.7.99.1.1a3, <7.0.7.99.2

So I'm able to install pvxslibs 1.3.1 standalone. But the required epicscorelibs versions are not compatible with the specified build requirements of p4p (epicscorelibs==7.0.7.99.0.2)

ffeldbauer commented 2 months ago
$ python3 --version ; uname -r -m
Python 3.11.2
6.6.47+rpt-rpi-v8 aarch64
$ python3 -m venv p4ptest
$ ./p4ptest/bin/pip install --log p4p_install_$(uname -m).log p4p==4.2.0a2

I tried it again and stored the output of the install process in a (verbose) logfile:

p4p_install_aarch64.log

EDIT: After reading the log file, I found the issue and it is similar to the above for p4p==4.1.12:

pvxslibs depends on epicscorelibs>=7.0.3.99.2.0a1, so pip uses the newest version it can find (7.0.7.99.1.1a3) to build and compile pvxslibs. But this version is too new for building and compiling p4p==4.2.0a2 && python==3.11.

In order to fix this, one has to somehow force pip to use epicscorelibs==7.0.7.99.0.2 when building pvxslibs....

juanfem commented 2 months ago

Your docker container is x86_64. For this architecture I was also able to install p4p 4.2.0a2 too.

right, sorry I took the wrong container. With another one that is linux-aarch64 I get the same issue:

      The conflict is caused by:
          The user requested epicscorelibs==7.0.7.99.0.2
          pvxslibs 1.3.1 depends on epicscorelibs<7.0.7.99.2 and >=7.0.7.99.1.1a3
ffeldbauer commented 2 months ago

On the positive side, I tried to install p4p with python3.12

$ docker run -it --platform linux/arm64 python:3.12-slim bash

and inside the container:

$ uname -m
aarch64
$ pip install -U pip
$ apt-get update && apt-get install -qqy --no-install-recommends gcc g++
[...]
$ pip install p4p==4.2.0a2
[...]
$ pip freeze
epicscorelibs==7.0.7.99.1.1a3
nose2==0.15.1
numpy==2.1.1
p4p==4.2.0a2
ply==3.11
pvxslibs==1.3.2a2
setuptools==75.1.0
setuptools-dso==2.11

Guess I will just use a docker container to run p4p on my Raspi instead of installing it in the host system

mdavidsaver commented 2 months ago

Looking at the pyproject files:

https://github.com/epics-base/p4p/blob/b1052f4886ccf65d5459f58293acb73afe1521fb/pyproject.toml#L9

I think you are correct that this is too strict.

pyproject.toml should have loose ranges based on build time requirements. Strict versioning for binaries should either be prepared by CI jobs, or injected into wheel builds by setup.py.

... see https://github.com/epics-base/p4p/pull/157

mdavidsaver commented 2 months ago

Ok...it seems to be (again) an issue with linux-aarch64:

Not entirely. A more representative test needs to avoid using wheels.

$ ./p4pdev/bin/pip install --no-binary epicscorelibs,pvxslibs,p4p p4p==4.2.0a2
...
      ERROR: Cannot install epicscorelibs==7.0.7.99.0.2 and pvxslibs==1.3.1 because these package versions have conflicting dependencies.
mdavidsaver commented 2 months ago

Please re-test with 4.2.0a3.

$ ./p4pdev/bin/pip install --no-binary epicscorelibs,pvxslibs,p4p p4p==4.2.0a3
...
Successfully installed epicscorelibs-7.0.7.99.1.1a3 nose2-0.15.1 numpy-2.1.1 p4p-4.2.0a3 ply-3.11 pvxslibs-1.3.1 setuptools-dso-2.11
ffeldbauer commented 2 months ago

I tested it on my raspi (the --no-binary has no effect here, as epicscorelibs, pvxslibs, and p4p have no prebuild wheels for aarch64)

pandadcs@raspberrypi:~ $ ./p4ptest/bin/pip install p4p==4.2.0a3
[...]
Successfully built p4p
Installing collected packages: ply, nose2, epicscorelibs, pvxslibs, p4p
Successfully installed epicscorelibs-7.0.7.99.1.1a3 nose2-0.15.1 p4p-4.2.0a3 ply-3.11 pvxslibs-1.3.1
juanfem commented 2 months ago

p4p-4.2.0a3 installs fine and passes all tests on linux-aarch64 for me as well. thanks!