-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[lldb-dap] Add support for launching supported clients #165941
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
base: main
Are you sure you want to change the base?
Conversation
|
Depends on #165925 |
|
@llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) ChangesSupport launching a supported DAP client using the lldb-dap binary. Here's an example: This will open the following URL with Fixes #125777 Full diff: https://github.com/llvm/llvm-project/pull/165941.diff 8 Files Affected:
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 7db334ca56bcf..fa940b7b73943 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -1,13 +1,11 @@
# We need to include the llvm components we depend on manually, as liblldb does
# not re-export those.
set(LLVM_LINK_COMPONENTS Support)
-set(LLVM_TARGET_DEFINITIONS Options.td)
-tablegen(LLVM Options.inc -gen-opt-parser-defs)
-add_public_tablegen_target(LLDBDAPOptionsTableGen)
add_lldb_library(lldbDAP
Breakpoint.cpp
BreakpointBase.cpp
+ ClientLauncher.cpp
CommandPlugins.cpp
DAP.cpp
DAPError.cpp
diff --git a/lldb/tools/lldb-dap/ClientLauncher.cpp b/lldb/tools/lldb-dap/ClientLauncher.cpp
new file mode 100644
index 0000000000000..301bbde61edb9
--- /dev/null
+++ b/lldb/tools/lldb-dap/ClientLauncher.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClientLauncher.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace lldb_dap;
+
+std::optional<ClientLauncher::Client>
+ClientLauncher::GetClientFrom(llvm::StringRef str) {
+ return llvm::StringSwitch<std::optional<ClientLauncher::Client>>(str.lower())
+ .Case("vscode", ClientLauncher::VSCode)
+ .Default(std::nullopt);
+}
+
+std::unique_ptr<ClientLauncher>
+ClientLauncher::GetLauncher(ClientLauncher::Client client) {
+ switch (client) {
+ case ClientLauncher::VSCode:
+ return std::make_unique<VSCodeLauncher>();
+ }
+ return nullptr;
+}
+
+static std::string URLEncode(llvm::StringRef in) {
+ std::string out;
+ llvm::raw_string_ostream os(out);
+ for (char c : in) {
+ if (std::isalnum(c) || llvm::StringRef("-_.~").contains(c))
+ os << c;
+ else
+ os << '%' << llvm::utohexstr(c, false, 2);
+ }
+ return os.str();
+}
+
+llvm::Error VSCodeLauncher::Launch(const std::vector<llvm::StringRef> args) {
+ std::vector<std::string> encoded_launch_args;
+ for (llvm::StringRef arg : args)
+ encoded_launch_args.push_back(URLEncode(arg));
+
+ const std::string args_str = llvm::join(args, "&args=");
+ const std::string launch_url = llvm::formatv(
+ "vscode://llvm-vs-code-extensions.lldb-dap/start?program={0}", args_str);
+ const std::string command =
+ llvm::formatv("code --open-url {0}", launch_url).str();
+
+ std::system(command.c_str());
+ return llvm::Error::success();
+}
diff --git a/lldb/tools/lldb-dap/ClientLauncher.h b/lldb/tools/lldb-dap/ClientLauncher.h
new file mode 100644
index 0000000000000..c9f249e00a8ca
--- /dev/null
+++ b/lldb/tools/lldb-dap/ClientLauncher.h
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_CLIENTLAUNCHER_H
+#define LLDB_TOOLS_LLDB_DAP_CLIENTLAUNCHER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <vector>
+
+namespace lldb_dap {
+
+class ClientLauncher {
+public:
+ enum Client {
+ VSCode,
+ };
+
+ virtual ~ClientLauncher() = default;
+ virtual llvm::Error Launch(const std::vector<llvm::StringRef> args) = 0;
+
+ static std::optional<Client> GetClientFrom(llvm::StringRef str);
+ static std::unique_ptr<ClientLauncher> GetLauncher(Client client);
+};
+
+class VSCodeLauncher final : public ClientLauncher {
+public:
+ using ClientLauncher::ClientLauncher;
+
+ llvm::Error Launch(const std::vector<llvm::StringRef> args) override;
+};
+
+} // namespace lldb_dap
+
+#endif
diff --git a/lldb/tools/lldb-dap/tool/CMakeLists.txt b/lldb/tools/lldb-dap/tool/CMakeLists.txt
index b39a4ed9c40e7..5335d25c5d450 100644
--- a/lldb/tools/lldb-dap/tool/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/tool/CMakeLists.txt
@@ -1,3 +1,7 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(LLDBDAPOptionsTableGen)
+
add_lldb_tool(lldb-dap
lldb-dap.cpp
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/tool/Options.td
similarity index 94%
rename from lldb/tools/lldb-dap/Options.td
rename to lldb/tools/lldb-dap/tool/Options.td
index 5e9dd7a1d6419..339a64fed6c32 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/tool/Options.td
@@ -82,3 +82,11 @@ def connection_timeout: S<"connection-timeout">,
"timeout is reached, the server will be closed and the process will exit. "
"Not specifying this argument or specifying non-positive values will "
"cause the server to wait for new connections indefinitely.">;
+
+def client
+ : S<"client">,
+ MetaVarName<"<client>">,
+ HelpText<
+ "Use lldb-dap as a launcher for a curated number of DAP client.">;
+
+def REM : R<["--"], "">;
diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
index 45caa1a81059b..f10ed12344cbd 100644
--- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "ClientLauncher.h"
#include "DAP.h"
#include "DAPLog.h"
#include "EventHelper.h"
@@ -141,6 +142,12 @@ static void PrintHelp(LLDBDAPOptTable &table, llvm::StringRef tool_name) {
debugger to attach to the process.
lldb-dap -g
+
+ You can also use lldb-dap to launch a supported client, for example the
+ LLDB-DAP Visual Studio Code extension.
+
+ lldb-dap --client vscode -- /path/to/binary <args>
+
)___";
}
@@ -150,6 +157,29 @@ static void PrintVersion() {
llvm::outs() << "liblldb: " << lldb::SBDebugger::GetVersionString() << '\n';
}
+static llvm::Error LaunchClient(const llvm::opt::InputArgList &args) {
+ auto *client_arg = args.getLastArg(OPT_client);
+ assert(client_arg && "must have client arg");
+
+ std::optional<ClientLauncher::Client> client =
+ ClientLauncher::GetClientFrom(client_arg->getValue());
+ if (!client)
+ return llvm::createStringError(
+ llvm::formatv("unsupported client: {0}", client_arg->getValue()));
+
+ std::vector<llvm::StringRef> launch_args;
+ if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {
+ for (auto *value : arg->getValues()) {
+ launch_args.push_back(value);
+ }
+ }
+
+ if (launch_args.empty())
+ return llvm::createStringError("no launch arguments provided");
+
+ return ClientLauncher::GetLauncher(*client)->Launch(launch_args);
+}
+
#if not defined(_WIN32)
struct FDGroup {
int GetFlags() const {
@@ -541,6 +571,14 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
+ if (input_args.hasArg(OPT_client)) {
+ if (llvm::Error error = LaunchClient(input_args)) {
+ llvm::WithColor::error() << llvm::toString(std::move(error)) << '\n';
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+ }
+
ReplMode default_repl_mode = ReplMode::Auto;
if (input_args.hasArg(OPT_repl_mode)) {
llvm::opt::Arg *repl_mode = input_args.getLastArg(OPT_repl_mode);
diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt
index a08414c30e6cd..b1fdef18fddba 100644
--- a/lldb/unittests/DAP/CMakeLists.txt
+++ b/lldb/unittests/DAP/CMakeLists.txt
@@ -1,4 +1,5 @@
add_lldb_unittest(DAPTests
+ ClientLauncherTest.cpp
DAPErrorTest.cpp
DAPTest.cpp
DAPTypesTest.cpp
diff --git a/lldb/unittests/DAP/ClientLauncherTest.cpp b/lldb/unittests/DAP/ClientLauncherTest.cpp
new file mode 100644
index 0000000000000..3099982c694fb
--- /dev/null
+++ b/lldb/unittests/DAP/ClientLauncherTest.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClientLauncher.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <optional>
+
+using namespace lldb_dap;
+using namespace llvm;
+
+TEST(ClientLauncherTest, GetClientFromVSCode) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("vscode");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(ClientLauncher::VSCode, result.value());
+}
+
+TEST(ClientLauncherTest, GetClientFromVSCodeUpperCase) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("VSCODE");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(ClientLauncher::VSCode, result.value());
+}
+
+TEST(ClientLauncherTest, GetClientFromVSCodeMixedCase) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("VSCode");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(ClientLauncher::VSCode, result.value());
+}
+
+TEST(ClientLauncherTest, GetClientFromInvalidString) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("invalid");
+ EXPECT_FALSE(result.has_value());
+}
+
+TEST(ClientLauncherTest, GetClientFromEmptyString) {
+ std::optional<ClientLauncher::Client> result =
+ ClientLauncher::GetClientFrom("");
+ EXPECT_FALSE(result.has_value());
+}
|
| using namespace lldb_dap; | ||
| using namespace llvm; | ||
|
|
||
| TEST(ClientLauncherTest, GetClientFromVSCode) { |
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.
Should we also include a URLEncode test? Either directly or by checking with calls to VSCodeLauncher::Launch?
Support launching a supported DAP client using the lldb-dap binary. Currently, only the official LLDB-DAP Visual Studio Code extension is supported. It uses the VS Code launch URL format. Fixes llvm#125777
45201f1 to
e595bff
Compare
| std::optional<ClientLauncher::Client> | ||
| ClientLauncher::GetClientFrom(llvm::StringRef str) { | ||
| return llvm::StringSwitch<std::optional<ClientLauncher::Client>>(str.lower()) | ||
| .Case("vscode", ClientLauncher::VSCode) |
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.
would be great to also have a "url" / "terminal" / "stdout" client which simply prints the URL to stdout. That would allow me to also use it with custom scripts
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.
although 🤔 I guess that even that would already be VSCode specific... So I guess it would have to be "vscode-url" instead of just "url"
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.
Done. This also made a good candidate for a Shell test.
Support launching a supported DAP client using the lldb-dap binary.
Currently, only the official LLDB-DAP Visual Studio Code extension is
supported. It uses the VS Code launch URL format.
Here's an example:
This will open the following URL with
code --open-url:Fixes #125777