aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lldb/test/Shell/DAP/TestClientLauncher.test2
-rw-r--r--lldb/tools/lldb-dap/CMakeLists.txt1
-rw-r--r--lldb/tools/lldb-dap/ClientLauncher.cpp74
-rw-r--r--lldb/tools/lldb-dap/ClientLauncher.h50
-rw-r--r--lldb/tools/lldb-dap/tool/Options.td8
-rw-r--r--lldb/tools/lldb-dap/tool/lldb-dap.cpp38
-rw-r--r--lldb/unittests/DAP/CMakeLists.txt1
-rw-r--r--lldb/unittests/DAP/ClientLauncherTest.cpp71
8 files changed, 245 insertions, 0 deletions
diff --git a/lldb/test/Shell/DAP/TestClientLauncher.test b/lldb/test/Shell/DAP/TestClientLauncher.test
new file mode 100644
index 0000000..a79a940
--- /dev/null
+++ b/lldb/test/Shell/DAP/TestClientLauncher.test
@@ -0,0 +1,2 @@
+# RUN: lldb-dap --client vscode-url -- /path/to/foo | FileCheck %s
+# CHECK: vscode://llvm-vs-code-extensions.lldb-dap/start?program=%2Fpath%2Fto%2Ffoo
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index dd1bbbd..fa940b7 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS Support)
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 0000000..4cac1d6
--- /dev/null
+++ b/lldb/tools/lldb-dap/ClientLauncher.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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)
+ .Case("vscode-url", ClientLauncher::VSCodeURL)
+ .Default(std::nullopt);
+}
+
+std::unique_ptr<ClientLauncher>
+ClientLauncher::GetLauncher(ClientLauncher::Client client) {
+ switch (client) {
+ case ClientLauncher::VSCode:
+ return std::make_unique<VSCodeLauncher>();
+ case ClientLauncher::VSCodeURL:
+ return std::make_unique<VSCodeURLPrinter>();
+ }
+ return nullptr;
+}
+
+std::string VSCodeLauncher::URLEncode(llvm::StringRef str) {
+ std::string out;
+ llvm::raw_string_ostream os(out);
+ for (char c : str) {
+ if (std::isalnum(c) || llvm::StringRef("-_.~").contains(c))
+ os << c;
+ else
+ os << '%' << llvm::utohexstr(c, false, 2);
+ }
+ return os.str();
+}
+
+std::string
+VSCodeLauncher::GetLaunchURL(const std::vector<llvm::StringRef> args) const {
+ assert(!args.empty() && "empty launch 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(encoded_launch_args, "&args=");
+ return llvm::formatv(
+ "vscode://llvm-vs-code-extensions.lldb-dap/start?program={0}",
+ args_str)
+ .str();
+}
+
+llvm::Error VSCodeLauncher::Launch(const std::vector<llvm::StringRef> args) {
+ const std::string launch_url = GetLaunchURL(args);
+ const std::string command =
+ llvm::formatv("code --open-url {0}", launch_url).str();
+
+ std::system(command.c_str());
+ return llvm::Error::success();
+}
+
+llvm::Error VSCodeURLPrinter::Launch(const std::vector<llvm::StringRef> args) {
+ llvm::outs() << GetLaunchURL(args) << '\n';
+ 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 0000000..780b178
--- /dev/null
+++ b/lldb/tools/lldb-dap/ClientLauncher.h
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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,
+ VSCodeURL,
+ };
+
+ 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 : public ClientLauncher {
+public:
+ using ClientLauncher::ClientLauncher;
+
+ llvm::Error Launch(const std::vector<llvm::StringRef> args) override;
+
+ std::string GetLaunchURL(const std::vector<llvm::StringRef> args) const;
+ static std::string URLEncode(llvm::StringRef str);
+};
+
+class VSCodeURLPrinter : public VSCodeLauncher {
+ using VSCodeLauncher::VSCodeLauncher;
+
+ llvm::Error Launch(const std::vector<llvm::StringRef> args) override;
+};
+
+} // namespace lldb_dap
+
+#endif
diff --git a/lldb/tools/lldb-dap/tool/Options.td b/lldb/tools/lldb-dap/tool/Options.td
index 5e9dd7a..339a64f 100644
--- a/lldb/tools/lldb-dap/tool/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 45caa1a..f10ed12 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 @@ EXAMPLES:
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 a08414c..b1fdef1 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 0000000..dbaf9ee
--- /dev/null
+++ b/lldb/unittests/DAP/ClientLauncherTest.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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());
+}
+
+TEST(ClientLauncherTest, URLEncode) {
+ EXPECT_EQ("", VSCodeLauncher::URLEncode(""));
+ EXPECT_EQ(
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~",
+ VSCodeLauncher::URLEncode("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST"
+ "UVWXYZ0123456789-_.~"));
+ EXPECT_EQ("hello%20world", VSCodeLauncher::URLEncode("hello world"));
+ EXPECT_EQ("hello%21%40%23%24", VSCodeLauncher::URLEncode("hello!@#$"));
+ EXPECT_EQ("%2Fpath%2Fto%2Ffile", VSCodeLauncher::URLEncode("/path/to/file"));
+ EXPECT_EQ("key%3Dvalue%26key2%3Dvalue2",
+ VSCodeLauncher::URLEncode("key=value&key2=value2"));
+ EXPECT_EQ("100%25complete", VSCodeLauncher::URLEncode("100%complete"));
+ EXPECT_EQ("file_name%20with%20spaces%20%26%20special%21.txt",
+ VSCodeLauncher::URLEncode("file_name with spaces & special!.txt"));
+ EXPECT_EQ("%00%01%02",
+ VSCodeLauncher::URLEncode(llvm::StringRef("\x00\x01\x02", 3)));
+ EXPECT_EQ("test-file_name.txt~",
+ VSCodeLauncher::URLEncode("test-file_name.txt~"));
+
+ // UTF-8 encoded characters should be percent-encoded byte by byte.
+ EXPECT_EQ("%C3%A9", VSCodeLauncher::URLEncode("é"));
+}