-
Notifications
You must be signed in to change notification settings - Fork 7
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
Continue Create info module for each DSO #15
Conversation
Generates an info module per DSO built with information on the original dot separated name, the stem and full path to the DSO. The info module also adds the directory containing the DSO to the DLL path on Windows once the info module is loaded. The name for the info module is configurable per DSO and defaults to `"{stem}_dso.py"`. Setting the name to `None` turns off info moduel generation for the DSO. The change does not attempt to create proper package structure to ensure the info modules are importable, that is up to the user.
@mdavidsaver There is a |
Thank you. This has tripped me up before... I'll have another go tomorrow. |
68a403c
to
f4ea2eb
Compare
I think I have things straightened out now, and the tests are passing. (and I've cleanup up my old virtualenvs) I needed to add an additional case to the generated info file to handle py <3.8 on windows (before New local build testing recipe
The added testing uses |
$ cat example/src/dsodemo/lib/demo_dso.py
# generated by setuptools_dso
import sys, os
# on windows, extend DLL search path to include
# the directory containing this file
def fixpath():
libdir = os.path.dirname(__file__)
if hasattr(os, 'add_dll_directory'): # py >= 3.8
os.add_dll_directory(libdir)
elif sys.platform == "win32":
path = os.environ.get('PATH', '').split(os.pathsep)
path.append(libdir)
os.environ['PATH'] = os.pathsep.join(path)
fixpath()
del fixpath
dsoname = 'dsodemo.lib.demo'
libname = 'libdemo.so'
soname = 'libdemo.so.1.0'
dir = os.path.dirname(__file__)
filename = os.path.join(dir, libname)
sofilename = os.path.join(dir, soname)
del dir
__all__ = ("dsoname", "libname", "soname", "filename", "sofilename") |
While not a breaking change, beginning to automatically injecting code into dependent packages is something which might surprise users (it would surprise me). I can't think of a way to communicate this other than through the version number, so this change will trigger a 2.0. |
We could also have this be opt-in behavior by having the name default to |
@mdavidsaver, thanks for asking. Maybe I'm not understanding something correctly, but let me try to share my thoughts. The problem we are trying to fix is this: if a DSO-A is built in project A and another project B needs to use it (by e.g. linking from DSO-B) there is a problem in how OS dynamic linker should find DSO-A when loading DSO-B. CTypes case is also related: we want to import a DSO but given py-import-like DSO path we don't know the correct OS-level path to DSO file. If this is indeed the problem, let me share that I actually already hit it in practice with my wendelin.core + libgolang work: wendelin.core (project A) builds For now I've worked it around by explicitly preloading So about the solution - my thoughs are this:
Appologize for sharing it a bit unstructured. It is hard to find time for me right now to think in more details about this, but I would suggest not to rush and to contemplate this whole topic until reasonable understanding that covers all practical cases is reached. Kirill |
I actually think these are two different problems. Solved the same way. But the current solution only addresses the second point: dynamically opening a library with Actionable comments for this PR:
I agree with renaming to
I'm not sure I follow this? Does the loader really consider two different objects with the same library name (e.g. Do we need to step back and consider the whole system together? Or can we piecemeal the problem and offer some of the features today? |
Is this windows, and making some additional
I'm also inclined towards emitting info files by default.
I hadn't considered this. You are right though, strictly speaking this process should be recursive to make all the needed
I'm also a bit confused by this. Can you elaborate on a specific situation? It may be that I'm making some assumption like eg. unqualified library names (really SONAMEs) will be unique in any given dependency chain. |
No piece of software is ever done :) So I won't be waiting for perfection. My immediate concern is with external interface. If possible, I'd like to avoid having to make a breaking change in another month. Right now, I see this mainly as the generated file name. eg. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably pedantic, but I prefer it.
@mdavidsaver, @ktbarrett, thanks for feedback. My point is too about not exposing a public interface we will want to change later and it will be hard to change because the information / API is already exposed and used by users. I mean we should keep I'm on Linux. Regarding fully qualified name: if a library is linked into libA.so and there are two dir1/libA.so and dir2/libA.so and have the same SONAME, the ld.so dynamic loader will use the first that is loaded into the process, or the first that is found via See e.g. here: kirr@deco:~$ objdump -p /bin/ls|grep NEEDED
NEEDED libselinux.so.1
NEEDED libc.so.6
kirr@deco:~$ ldd /bin/ls
linux-vdso.so.1 (0x00007ffded8d3000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fdaac4be000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdaac2f9000)
libpcre2-8.so.0 => /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007fdaac261000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fdaac25b000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdaac546000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fdaac239000) /bin/ls is linked to In summary, I suggest:
Then we can step-by-step incrementally adjust it for handling DSO -> DSO dependencies in the future. Hope it clarifies things a bit and sorry for in-hurry writings, |
@ktbarrett Your comments above are quite specific. It you've already made the relevant changes, please push to #14. |
@mdavidsaver Addressed. Also fixed up the test script. |
I've made an attempt at this. The results are new functions So from setuptools_dso import prepare_dso
prepare_dso('..lib.demo') and a dynamic lookup would be: demolib = ctypes.CDLL(setuptools_dso.find_dso('dsodemo.lib.demo', so=True), ctypes.RTLD_GLOBAL) |
Should these two really have the same SONAME? The convention is the identical SONAME implies ~equivalent content. While ugly, would it be workable for you to encode package name in the SONAME? eg. |
@mdavidsaver, thanks.
My point was that such encoding should be done automatically by setuptools_dso by naming DSO file with fully-qualified name of DSO. Then it will work universally and bridge gap in between python and dynamic linker namespaces. This is what Question: why do we need to distinguish filename and sofilename? I mean in which use-cases I also suggest to consider renaming |
I'm having trouble articulating exactly why, but this seems a strange solution to me. Maybe I'm being overly conservative. More immediately, it would be an incompatible change, so I don't want to make this a default at present. Making this an option would be fine though. Maybe
I haven't been able to think of a good use case for a
Ok, I think I see the potential for confusion. The runtime is being prepared, not the DSO itself. FYI. I'm planning to merge this PR soon, and will then upload a pre-release to pypi.org (eg. |
Since _dsoinfo.py is "standard" convention, only allow specific override (or omission).
65f8ab0
to
912a8e1
Compare
Ok. 2.0a1 is uploaded, also expanded documentation. @ktbarrett Thanks for your contribution (ideas and code). Two final changes. Renamed |
Thanks, @mdavidsaver; thanks @ktbarrett. |
I followed up on #11 (comment) |
setuptools_dso 2 started to emit those autogenerated files. See epics-base/setuptools_dso#15 for details.
setuptools_dso 2 started to emit those autogenerated files. See epics-base/setuptools_dso#15 for details.
Extension of #14. Add SONAME to info file and attempt to test. The new test is currently failing on CI due to omission of the generated info file. Which is strange since my local builds with
pip wheel
andsetup.py bdist_wheel
include it. Not sure what is going on.Local testing with
The last shows:
The CI logs show
@ktbarrett