New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BUG: Undefined symbol when compiling shared library for numpy 2.0 #26091
Comments
What update? |
Before the migration to Numpy 2.0.0b1. |
This comment was marked as outdated.
This comment was marked as outdated.
Yes, that is what I was afraid of... I was hoping it could be useful for someone involved in the development of numpy 2.0.
I'm building everything myself from source, so yes I'm sure I'm using the exact same numpy version (2.0.0b1). |
What I don't understand is that |
This comment was marked as outdated.
This comment was marked as outdated.
OK I think the difference is here: Numpy 2.0.0b1#if defined(NO_IMPORT) || defined(NO_IMPORT_ARRAY)
extern void **PyArray_API;
extern int PyArray_RUNTIME_VERSION;
#else
#if defined(PY_ARRAY_UNIQUE_SYMBOL)
void **PyArray_API;
int PyArray_RUNTIME_VERSION;
#else
static void **PyArray_API = NULL;
static int PyArray_RUNTIME_VERSION = 0;
#endif
#endif Numpy 1.26.4#if defined(PY_ARRAY_UNIQUE_SYMBOL)
#define PyArray_API PY_ARRAY_UNIQUE_SYMBOL
#endif
#if defined(NO_IMPORT) || defined(NO_IMPORT_ARRAY)
extern void **PyArray_API;
#else
#if defined(PY_ARRAY_UNIQUE_SYMBOL)
void **PyArray_API;
#else
static void **PyArray_API=NULL;
#endif
#endif |
Here Is what Eigenpy is doing: #ifndef PY_ARRAY_UNIQUE_SYMBOL
#define PY_ARRAY_UNIQUE_SYMBOL EIGENPY_ARRAY_API
#endif
#include <numpy/numpyconfig.h>
#ifdef NPY_1_8_API_VERSION
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#endif
#include <numpy/ndarrayobject.h>
#include <numpy/ufuncobject.h> Does it make sense to you or is it hard to tell ? |
The point is that you use Eigens(?) If that is a serious use-case I have to think a bit how to deal with it. |
So eigenpy is defining
which presumably your build hits. I'm not sure offhand if we test this sort of build, ping @seberg since he did this work on adding |
It is indeed defining #define NO_IMPORT_ARRAY
#include "eigenpy/numpy.hpp"
#undef NO_IMPORT_ARRAY
Eigenpy has been recompiled with NumPy 2.
It is hard to tell for sure if this is intended or not... But using Eigenpy to expose Eigen for other libraries is definitely a serious usecase. The library is used by pinocchio, hpp-fcl, crocoddyl... and many others INRIA projects. |
I think this pattern is coming from |
Yeah, but boost python is header only. While here it seems this isn't a single compilation unit. At least that is my only explanation. The only way this makes sense to me is that for some reason you are finding an older So, I am not clear whether we need a solution that always works, a work-around for these odder cases where it is necessary for some reason, or nothing at all and some part of the project just has not been recompiled with NumPy 2 (e.g. compilation cache). |
Indeed ! This is it... I'm explicitly doing this and I forgot about it... Sorry about that. |
Closing then, let us know in a new issue if you have any other issues handling numpy 2.0. |
Good point. Is it difficult to make it private API ? Maybe I should not support this scenario in the first place, but I would love to be able to let the user use its own version of eigenpy at runtime without causing such undefined symbol error. It was working fine so far. |
You want me to open a new issue to continue this discussion ? If you guys have any advice about how this scenario should be handled. |
Ah sorry for closing a little hastily. I thought you didn't want to do this. So I think what will work going forward is to build against numpy 2.0, and then use those builds to support numpy 1.x and numpy 2.0. The numpy ABI is now backward compatible - we can expose older ABIs if you compile against newer numpy. It's not possible for the other way to work, so I don't think what you're trying to do will work with numpy 2.0. Maybe sebastian can comment further. |
Yes, this is what I am currently doing, with great success actually ! What does not work is:
This scenario makes sense in the context of Boost::Python actually, the "global" registry of from-to python converter works requires to have a unique instance of boost python shared liabry to be loaded at the same time, otherwise inter-operability between modules would be broken. |
I don't know? Within the private API, you can still include Most of the stuff in the
I don't know. It seems to me that leaking NumPy API out is bad (i.e. any include of We can easily work around this, by just defining But a caveat remains: The caller of That is a potential issue, but I guess one we just have to live with, and honestly, one that I suspect nobody is likely to run into in practice. |
Thank you for your very insightful rely! Now I understand better how to get around the issue. I will try to propose a MR for eigenpy that makes it private and see if it helps.
I'm not 100% sure if this means that my usecase is not supported, but i can assure you that the problem I'm running into is real. My library is one among many other relying on Eigenpy. Mine is the only one related to INRIA that got some traction. From this perspective, i want to be able to ensure inter-operability with all of them without requiring the user to build my library from source while all the others are already distributed as pre-compiled binaries. It is easy for me to add as requirement a specific eigenpy version, but I have no way to enforce the version of numpy used to compile it. This means that I have to encourage all the maintainers of all these projects to distributed wheels compiled against numpy 2.0 if I want to be able to do the same with mine. That is going to be challenging... i would say a "dirty" workaround would be fine for me. |
Well we'll need to find a solution whether in NumPy or not, but it is still unclear where/what that is. I think that if you just add a n (I.e. you would get a static define for these symbols, and would be expected to call I still suspect (I wish I there was a better way to do this unique symbol dance.) |
Unfortunately, this is a wrong assumption. Both Eigenpy and my own library are using barebone C-API of Numpy because not everything has been exposed in |
Let me summarize the situation here a bit:
This means that:
"Simple" Solution: We can easily redefine
both of which should be fine in practice. Problem:
So we can change the above. Things will work, but you lose those checks and balances. I suspect we can live with it, but... ... What bothers me most, is not what we do right now, but that I would like a way forward so that For example, a pattern of |
Thank you @seberg , all these details are helping a lot. I will wait for a feedback from eigenpy team before deciding going for one direction or the other. |
One new observation: Windows defaults to private symbols for shared libraries. Thus, it seems like your code is only working because you force static linking of |
Indeed I do, but only for historically reason (it was easier at the beginning to ensure that the C module can be loaded on any machine if it has no external dependency at runtime). Now, I'm "repairing" the generated wheel using |
(sorry for the long post...)
No, this is the reason for this old PR stack-of-tasks/eigenpy@7a57c19, but that isn't enough at all, since
I also do wonder how much you actually care about this behavior: The wheels you package must include For NumPy, I think we ideally "break" Now, since compilers optimize things away that are unused, I could imagine things are actually somewhat fine after that, although it would be nice if That will mean that some Of course It might be nice to simplify the include pattern a bit at least for C++, I can imagine that would be easy (i.e. a bit similar to what I also wish we had a |
Yes, I agree with you. What I meant is that I just never tried to link dynamically because I wanted to link statically from the start to improve portability. I never had in mind to link dynamically. But yes for sure, it would not work anyway. |
I am considering escalating this a bit for you (because I think it clarifies things and is generally better), see gh-26103. But I am not sure... I am also fine with just "helping" you here (could be tied into that same define also). Although I am not even sure that the failing use-case is important (dynamically linking with an eigenpy that was compiled with an older NumPy version than your own library). |
Describe the issue:
I am getting some undefined symbol error when compiling a shared library (C Python Module) that is linking against yet another shared library (C Python Module), both compiled against Numpy 2.0. Everything was fine before the update of Numpy to 2.0.0b1.
Does anyone have any idea regarding what is causing this issue?
Reproduce the code example:
I don't have a MRE. I'm running the following Github Action pipeline
I could write one if really necessary. Basically I compile the library Eigenpy and then I linked my own module on it. It is the latter that contains undefined symbols.
Error message:
Python and NumPy Versions:
Python 3.10, Numpy 2.0.0b1
Runtime Environment:
No response
Context for the issue:
No response
The text was updated successfully, but these errors were encountered: