Skip to content

[lldb] Fix deps loading for lldb-python on Windows and Python3.8+ #9370

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

Merged
merged 7 commits into from
Oct 11, 2024

Conversation

weliveindetail
Copy link
Member

Since Python3.8 the Windows Python runtime now longer loads dependent DLLs for module imports from Path or CWD. Instead, candidate locations must be set explicit via os.add_dll_directory(). This is a known issue and reported a long time ago. It's an issue for swiftlang LLDB in particular, because it depends on a number of DLLs from the Swift toolchain, like SwiftSyntax.dll and SwiftParser.dll.

Previously, the implicit assumption was that these DLLs will always be somewhere in Path and so Python would find them. Now we have to remember where to look and it might not always be the same places. We want to refer to the build-tree when testing and the install-tree after packaging. A list of candidate locations seems to be a flexible solution.

The list should be easy to write from CMake as well as easy to review and extend. A text file inside the Python package seems handy and it keeps the Python bindings code somehow generic. A post-build step for the lldb-python target creates the text file and sets LLVM's shared library output dir (i.e. the build-tree's bin) as the default location.

@weliveindetail
Copy link
Member Author

At this point, dotest.py tests sucessfully import _lldb from the build-tree again. I will look into packaging/install-tree requirements next, but aimed for early feedback.

I patched the module load code for both cases here, swig and static. However, upstream LLDB doesn't have the static binding case. Would it be better to submit the majority of this patch upstream and only patch the static binding code here in swiftlang LLDB? Not sure. Any thoughts?

@labath
Copy link

labath commented Oct 3, 2024

If you ask me, this solution feels too generic. I don't know how swift build is set up (let alone swift-on-windows), so it's hard to provide more specific feedback. Maybe you could give an example of the layouts (build and install) that you want to support.

Also, how is this situation handled on non-windows operating systems? Do we use rpath to let liblldb find the dependent binaries?

@weliveindetail
Copy link
Member Author

Also, how is this situation handled on non-windows operating systems? Do we use rpath to let liblldb find the dependent binaries?

Good question. In Apple Swift 5.10, liblldb has an rpath for the Swift dylibs in @loader_path/Libraries/usr/lib/swift/host and load commands like @rpath/libSwiftSyntax.dylib and @rpath/libSwiftParser.dylib (all inside LLDB.framework).

Homebrew does a llvm-shared-lib build and it doesn't ship LLDB.framework, so this might be closer to the Linux distribution. It doesn't have the Swift dependencies, because it ships upstream LLDB, but it has others. Looking at 17.0.6, it depends on @rpath/libclang-cpp.dylib, @rpath/libLLVM.dylib and Python which it hardcodes to /usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.12/Python on my system.

Installing the official LLDB 19 for Windows, I only see Python as a dynamic dependency. The DLL is 180MB on my system, so I guess everything else is linked statically.

> dumpbin /dependents "C:\Program Files\LLVM\bin\liblldb.dll"
...
File Type: DLL

  Image has the following dependencies:

    python310.dll
    KERNEL32.dll
    PSAPI.DLL
    ntdll.dll
    ...

Python seems special in this case: It's not resolved when running import lldb in standalone Python. I assume that is because it's loaded already. So, we have no orthogonal case in upstream LLVM right now.

For Swift, however, we don't want to link the DLLs statically.

@weliveindetail
Copy link
Member Author

I did a few more attempts to patch the module load code. For example, trying to hardcode a set of relative paths into the __init__.py scripts via CMake configure_file(). None of them really worked out. For the most part, because the process of generating the Python API is so complicated. On the example: The respective python.swig/lldb.py file would be in the build-tree now, instead of the source-tree and that info would need to be patched in locations like lldb/bindings/prepare_bindings.py etc. This seems like an unreasonable effort.

My interim status is: Attempting to patch module load code with os.add_dll_directory(), we won't get anything better than what was proposed above. So, what else could we do?

@weliveindetail
Copy link
Member Author

On Windows there is no concept similar to rpaths. Instead the best practice is to copy DLLs into the directory of their depending binaries. We attempted it before in the build script, but asking devs to keep the list of files up-to-date didn't seem feasible:
https://github.com/swiftlang/swift/pull/75946/files#diff-c861caa2fcc08744108e542ca836b07e342f537566e8c5f7a21a0e9125331ed9R1381-R1414

Instead of the build-script, we could also do that in CMake and it turns out that there even is a dedicated generator expression for it:
https://cmake.org/cmake/help/git-stage/manual/cmake-generator-expressions.7.html#genex:TARGET_RUNTIME_DLLS

That avoids the explicit file list and fixes the dotest.py tests for me!

@weliveindetail
Copy link
Member Author

weliveindetail commented Oct 7, 2024

TARGET_RUNTIME_DLLS requires CMake 3.21, but globally we are still at 3.20. Thus, I guarded it with an explicit version check for now. Maybe that's acceptable for the moment, given that so far the test suite didn't work at all on Windows? (We could run a CMake version bump RFC like this one while stabilizing.) What do you think?

@weliveindetail weliveindetail marked this pull request as ready for review October 8, 2024 13:55
weliveindetail added a commit to weliveindetail/llvm-project that referenced this pull request Oct 10, 2024
@weliveindetail
Copy link
Member Author

On Windows there is no concept similar to rpaths. Instead the best practice is to copy DLLs into the directory of their depending binaries. CMake has a dedicated generator expression for it.

That was the gist. What do you think @labath? Or otherwise @hjyamauchi and @hyp?

@labath
Copy link

labath commented Oct 11, 2024

If that's the state of the art on windows then I guess that's fine with me.

My question about was more along the lines of: if we know what rpath to set (are we setting that explicitly somewhere), then maybe it would be reasonable to take the same path and give it to python as the dll directory path (I'm not sure which mechanism to use, though if it would be known to cmake, then it could probably be somehow embedded in the bindings).

We have to copy it over in build.ps1 since it doesn't exist yet during the initial build.
@hjyamauchi hjyamauchi merged commit 2463be2 into swiftlang:next Oct 11, 2024
@weliveindetail weliveindetail deleted the lldb-python-deps-paths-fix branch October 12, 2024 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants