diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 71298d76ed4d6..2d97218cd3f55 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -113,6 +113,12 @@ if (LLDB_ENABLE_PYTHON) set(LLDB_PYTHON_EXT_SUFFIX "_d${LLDB_PYTHON_EXT_SUFFIX}") endif() endif() + if(TARGET Python3::Python) + get_target_property(_Python3_LIB_PATH Python3::Python IMPORTED_LIBRARY_LOCATION) + if(_Python3_LIB_PATH) + get_filename_component(LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME "${_Python3_LIB_PATH}" NAME) + endif() + endif() endif () if (LLDB_ENABLE_LUA) diff --git a/lldb/cmake/modules/AddLLDB.cmake b/lldb/cmake/modules/AddLLDB.cmake index 154123bbadb86..8de289468b232 100644 --- a/lldb/cmake/modules/AddLLDB.cmake +++ b/lldb/cmake/modules/AddLLDB.cmake @@ -248,6 +248,12 @@ function(add_lldb_executable name) ) target_link_libraries(${name} PRIVATE ${ARG_LINK_LIBS}) + if(WIN32) + list(FIND ARG_LINK_LIBS liblldb LIBLLDB_INDEX) + if(NOT LIBLLDB_INDEX EQUAL -1) + target_link_options(${name} PRIVATE "/DELAYLOAD:$.dll") + endif() + endif() if(CLANG_LINK_CLANG_DYLIB) target_link_libraries(${name} PRIVATE clang-cpp) else() diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt index 5c0cac484cc94..d02d0ab22d60c 100644 --- a/lldb/tools/driver/CMakeLists.txt +++ b/lldb/tools/driver/CMakeLists.txt @@ -34,6 +34,13 @@ add_dependencies(lldb ${tablegen_deps} ) +if(DEFINED LLDB_PYTHON_DLL_RELATIVE_PATH) + target_compile_definitions(lldb PRIVATE LLDB_PYTHON_DLL_RELATIVE_PATH="${LLDB_PYTHON_DLL_RELATIVE_PATH}") +endif() +if(DEFINED LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME) + target_compile_definitions(lldb PRIVATE LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME="${LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME}") +endif() + if(LLDB_BUILD_FRAMEWORK) # In the build-tree, we know the exact path to the framework directory. # The installed framework can be in different locations. diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 76288bd3019d4..1be3969538dd6 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -22,7 +22,10 @@ #include "lldb/Host/MainLoop.h" #include "lldb/Host/MainLoopBase.h" #include "lldb/Utility/Status.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" @@ -30,6 +33,10 @@ #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" +#ifdef _WIN32 +#include "llvm/Support/Windows/WindowsSupport.h" +#endif + #include #include #include @@ -436,6 +443,91 @@ SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) { return error; } +#ifdef _WIN32 +#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH +/// Returns the full path to the lldb.exe executable. +inline std::wstring GetPathToExecutableW() { + // Iterate until we reach the Windows API maximum path length (32,767). + std::vector buffer; + buffer.resize(MAX_PATH /*=260*/); + while (buffer.size() < 32767) { + if (GetModuleFileNameW(NULL, buffer.data(), buffer.size()) < buffer.size()) + return std::wstring(buffer.begin(), buffer.end()); + buffer.resize(buffer.size() * 2); + } + return L""; +} + +/// \brief Resolve the full path of the directory defined by +/// LLDB_PYTHON_DLL_RELATIVE_PATH. If it exists, add it to the list of DLL +/// search directories. +/// \return `true` if the library was added to the search path. +/// `false` otherwise. +bool AddPythonDLLToSearchPath() { + std::wstring modulePath = GetPathToExecutableW(); + if (modulePath.empty()) + return false; + + SmallVector utf8Path; + if (sys::windows::UTF16ToUTF8(modulePath.c_str(), modulePath.length(), + utf8Path)) + return false; + sys::path::remove_filename(utf8Path); + sys::path::append(utf8Path, LLDB_PYTHON_DLL_RELATIVE_PATH); + sys::fs::make_absolute(utf8Path); + + SmallVector widePath; + if (sys::windows::widenPath(utf8Path.data(), widePath)) + return false; + + if (sys::fs::exists(utf8Path)) + return SetDllDirectoryW(widePath.data()); + return false; +} +#endif + +#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME +/// Returns whether `python3x.dll` is in the DLL search path. +bool IsPythonDLLInPath() { +#define WIDEN2(x) L##x +#define WIDEN(x) WIDEN2(x) + WCHAR foundPath[MAX_PATH]; + DWORD result = + SearchPathW(nullptr, WIDEN(LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME), nullptr, + MAX_PATH, foundPath, nullptr); +#undef WIDEN2 +#undef WIDEN + + return result > 0; +} +#endif + +/// Try to setup the DLL search path for the Python Runtime Library +/// (python3xx.dll). +/// +/// If `LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME` is set, we first check if +/// python3xx.dll is in the search path. If it's not, we try to add it and +/// check for it a second time. +/// If only `LLDB_PYTHON_DLL_RELATIVE_PATH` is set, we try to add python3xx.dll +/// to the search path python.dll is already in the search path or not. +void SetupPythonRuntimeLibrary() { +#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME + if (IsPythonDLLInPath()) + return; +#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH + if (AddPythonDLLToSearchPath() && IsPythonDLLInPath()) + return; +#endif + llvm::errs() << "error: unable to find '" + << LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME << "'.\n"; + return; +#elif defined(LLDB_PYTHON_DLL_RELATIVE_PATH) + if (!AddPythonDLLToSearchPath()) + llvm::errs() << "error: unable to find the Python runtime library.\n"; +#endif +} +#endif + std::string EscapeString(std::string arg) { std::string::size_type pos = 0; while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) { @@ -738,6 +830,10 @@ int main(int argc, char const *argv[]) { "~/Library/Logs/DiagnosticReports/.\n"); #endif +#ifdef _WIN32 + SetupPythonRuntimeLibrary(); +#endif + // Parse arguments. LLDBOptTable T; unsigned MissingArgIndex;