Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ bool AddPythonDLLToSearchPath();
/// can be loaded. If successful, returns immediately. Otherwise, attempts to
/// resolve the relative path and add it to the DLL search path, then checks
/// again if python3xx.dll can be loaded.
llvm::Error SetupPythonRuntimeLibrary();
///
/// \return If LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME is defined, return the
/// absolute path of the Python shared library which was resolved or an error if
/// it could not be found. If LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME and
/// LLDB_PYTHON_DLL_RELATIVE_PATH are not defined, return an empty string.
llvm::Expected<std::string> SetupPythonRuntimeLibrary();

// BEGIN SWIFT
extern const std::string g_python_installation_note;
Expand Down
27 changes: 16 additions & 11 deletions lldb/source/Host/windows/PythonPathSetup/PythonPathSetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@

using namespace llvm;

#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
static std::string GetModulePath(HMODULE module) {
std::vector<WCHAR> buffer(MAX_PATH);
while (buffer.size() <= PATHCCH_MAX_CCH) {
DWORD len = GetModuleFileNameW(NULL, buffer.data(), buffer.size());
DWORD len = GetModuleFileNameW(module, buffer.data(), buffer.size());
if (len == 0)
return "";
if (len < buffer.size()) {
Expand All @@ -40,7 +41,6 @@ static std::string GetModulePath(HMODULE module) {
/// Returns the full path to the lldb.exe executable.
static std::string GetPathToExecutable() { return GetModulePath(NULL); }

#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
bool AddPythonDLLToSearchPath() {
std::string path_str = GetPathToExecutable();
if (path_str.empty())
Expand All @@ -62,26 +62,31 @@ bool AddPythonDLLToSearchPath() {
#endif

#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME
bool IsPythonDLLInPath() {
std::optional<std::string> GetPythonDLLPath() {
#define WIDEN2(x) L##x
#define WIDEN(x) WIDEN2(x)
HMODULE h = LoadLibraryW(WIDEN(LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME));
if (!h)
return false;
return std::nullopt;

std::string path = GetModulePath(h);
FreeLibrary(h);
return true;

return path;
#undef WIDEN2
#undef WIDEN
}
#endif

llvm::Error SetupPythonRuntimeLibrary() {
llvm::Expected<std::string> SetupPythonRuntimeLibrary() {
#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME
if (IsPythonDLLInPath())
return Error::success();
if (std::optional<std::string> python_path = GetPythonDLLPath())
return *python_path;
#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
if (AddPythonDLLToSearchPath() && IsPythonDLLInPath())
return Error::success();
if (AddPythonDLLToSearchPath()) {
if (std::optional<std::string> python_path = GetPythonDLLPath())
return *python_path;
}
#endif
return createStringError(
inconvertibleErrorCode(),
Expand All @@ -91,7 +96,7 @@ llvm::Error SetupPythonRuntimeLibrary() {
return createStringError(inconvertibleErrorCode(),
"unable to find the Python runtime library");
#endif
return Error::success();
return "";
}

// BEGIN SWIFT
Expand Down
6 changes: 4 additions & 2 deletions lldb/tools/driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,8 +741,10 @@ int main(int argc, char const *argv[]) {
#endif

#ifdef _WIN32
if (llvm::Error error = SetupPythonRuntimeLibrary()) {
llvm::WithColor::error() << llvm::toString(std::move(error)) << '\n';
auto python_path_or_err = SetupPythonRuntimeLibrary();
if (!python_path_or_err) {
llvm::WithColor::error()
<< llvm::toString(std::move(python_path_or_err.takeError())) << '\n';
// BEGIN SWIFT
llvm::WithColor::note() << g_python_installation_note;
return 1;
Expand Down
24 changes: 20 additions & 4 deletions lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from "vscode";
import * as child_process from "child_process";
import * as util from "util";
import * as os from "os";
import { LLDBDapServer } from "./lldb-dap-server";
import { createDebugAdapterExecutable } from "./debug-adapter-factory";
import { ConfigureButton, showErrorMessage } from "./ui/show-error-message";
Expand Down Expand Up @@ -70,8 +71,7 @@ const configurations: Record<string, DefaultConfig> = {
};

export class LLDBDapConfigurationProvider
implements vscode.DebugConfigurationProvider
{
implements vscode.DebugConfigurationProvider {
constructor(
private readonly server: LLDBDapServer,
private readonly logger: vscode.LogOutputChannel,
Expand Down Expand Up @@ -110,7 +110,7 @@ export class LLDBDapConfigurationProvider
);
this.logger.debug(
"Initial debug configuration:\n" +
JSON.stringify(debugConfiguration, undefined, 2),
JSON.stringify(debugConfiguration, undefined, 2),
);
let config = vscode.workspace.getConfiguration("lldb-dap");
for (const [key, cfg] of Object.entries(configurations)) {
Expand Down Expand Up @@ -195,6 +195,22 @@ export class LLDBDapConfigurationProvider
return undefined;
}

if (os.platform() === "win32") {
const pythonCheckProcess = child_process.spawnSync(executable.command, [
"--check-python",
]);
if (pythonCheckProcess.status !== 0) {
await vscode.window.showErrorMessage(
"Python is not installed correctly. Please install it to use lldb-dap.",
{
modal: true,
detail: pythonCheckProcess.stderr?.toString() ?? "",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the stderr suggest a remedy? If not is there documentation published anywhere with minimum versions, etc? We sometimes over a notification button that when clicked opens documentation

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what it currently looks like:

Screenshot 2026-05-01 at 17 15 15

In Swiftlang, we also have a note that's displayed explaining to the user which version of Python to install and where to get it:

const std::string g_python_installation_note =
"Ensure Python " LLDB_PYTHON_VERSION " "
#if defined(_M_ARM64)
"(arm64)"
#elif defined(_M_AMD64)
"(x64)"
#endif
" is installed and available in your Path.\n"
"Pre-built binaries are available at "
#if defined(_M_ARM64)
"https://nuget.org (search 'python')\n";
#elif defined(_M_AMD64)
"https://python.org\n";
#endif

It's printed as an llvm::note which goes to stderr. The modal window above will have that additional text.

},
);
return undefined;
}
}

// Server mode needs to be handled here since DebugAdapterDescriptorFactory
// will show an unhelpful error if it returns undefined. We'd rather show a
// nicer error message here and allow stopping the debug session gracefully.
Expand Down Expand Up @@ -222,7 +238,7 @@ export class LLDBDapConfigurationProvider

this.logger.info(
"Resolved debug configuration:\n" +
JSON.stringify(debugConfiguration, undefined, 2),
JSON.stringify(debugConfiguration, undefined, 2),
);

return debugConfiguration;
Expand Down
4 changes: 4 additions & 0 deletions lldb/tools/lldb-dap/tool/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def: Flag<["-"], "v">,
Alias<version>,
HelpText<"Alias for --version">;

def check_python : F<"check-python">,
HelpText<"Prints the path to the resolved Python DLL. "
"(Windows only).">;

def wait_for_debugger: F<"wait-for-debugger">,
HelpText<"Pause the program at startup.">;
def: Flag<["-"], "g">,
Expand Down
43 changes: 33 additions & 10 deletions lldb/tools/lldb-dap/tool/lldb-dap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,16 +475,6 @@ int main(int argc, char *argv[]) {
"~/Library/Logs/DiagnosticReports/.\n");
#endif

#ifdef _WIN32
if (llvm::Error error = SetupPythonRuntimeLibrary()) {
llvm::WithColor::error() << llvm::toString(std::move(error)) << '\n';
// BEGIN SWIFT
llvm::WithColor::note() << g_python_installation_note;
return 1;
// END SWIFT
}
#endif

llvm::SmallString<256> program_path(argv[0]);
llvm::sys::fs::make_absolute(program_path);
DAP::debug_adapter_path = program_path;
Expand All @@ -504,6 +494,39 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}

#ifdef _WIN32
if (input_args.hasArg(OPT_check_python)) {
#ifndef LLDB_ENABLE_PYTHON
llvm::errs() << "lldb-dap was not built with Python support" << '\n';
return EXIT_SUCCESS;
#endif
auto python_path_or_err = SetupPythonRuntimeLibrary();
if (!python_path_or_err) {
llvm::WithColor::error()
<< llvm::toString(python_path_or_err.takeError()) << '\n';
return EXIT_FAILURE;
}
std::string python_path = *python_path_or_err;
if (python_path.empty()) {
llvm::WithColor::error()
<< "unable to look for the Python shared library" << '\n';
return EXIT_FAILURE;
}
llvm::outs() << python_path << '\n';
return EXIT_SUCCESS;
}

auto python_path_or_err = SetupPythonRuntimeLibrary();
if (!python_path_or_err) {
llvm::WithColor::error()
<< llvm::toString(std::move(python_path_or_err.takeError())) << '\n';
// BEGIN SWIFT
llvm::WithColor::note() << g_python_installation_note;
return 1;
// END SWIFT
}
#endif

ReplMode default_repl_mode = ReplMode::Auto;
if (input_args.hasArg(OPT_repl_mode)) {
llvm::opt::Arg *repl_mode = input_args.getLastArg(OPT_repl_mode);
Expand Down