Author: Med Ismail Bennani Date: 2020-09-02T16:36:10+02:00 New Revision: addb5148f58d710fcaba04bb2afec8006ae8ac15
URL: https://github.com/llvm/llvm-project/commit/addb5148f58d710fcaba04bb2afec8006ae8ac15 DIFF: https://github.com/llvm/llvm-project/commit/addb5148f58d710fcaba04bb2afec8006ae8ac15.diff LOG: [lldb/Target] Add custom interpreter option to `platform shell` This patch adds the ability to use a custom interpreter with the `platform shell` command. If the user set the `-s|--shell` option with the path to a binary, lldb passes it down to the platform's `RunShellProcess` method and set it as the shell to use in `ProcessLaunchInfo to run commands. Note that not all the Platforms support running shell commands with custom interpreters (i.e. RemoteGDBServer is only expected to use the default shell). This patch also makes some refactoring and cleanups, like swapping CString for StringRef when possible and updating `SBPlatformShellCommand` with new methods and a new constructor. rdar://67759256 Differential Revision: https://reviews.llvm.org/D86667 Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com> Added: lldb/test/API/commands/platform/basic/Makefile lldb/test/API/commands/platform/basic/myshell.c Modified: lldb/bindings/interface/SBPlatform.i lldb/include/lldb/API/SBPlatform.h lldb/include/lldb/Host/Host.h lldb/include/lldb/Target/Platform.h lldb/include/lldb/Target/RemoteAwarePlatform.h lldb/source/API/SBPlatform.cpp lldb/source/Commands/CommandObjectPlatform.cpp lldb/source/Commands/Options.td lldb/source/Host/common/Host.cpp lldb/source/Host/macosx/objcxx/Host.mm lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h lldb/source/Target/Platform.cpp lldb/source/Target/RemoteAwarePlatform.cpp lldb/test/API/commands/platform/basic/TestPlatformCommand.py lldb/test/API/commands/platform/basic/TestPlatformPython.py Removed: ################################################################################ diff --git a/lldb/bindings/interface/SBPlatform.i b/lldb/bindings/interface/SBPlatform.i index 81945222c059..07aecfc354bb 100644 --- a/lldb/bindings/interface/SBPlatform.i +++ b/lldb/bindings/interface/SBPlatform.i @@ -45,6 +45,7 @@ public: class SBPlatformShellCommand { public: + SBPlatformShellCommand (const char *shell, const char *shell_command); SBPlatformShellCommand (const char *shell_command); SBPlatformShellCommand (const SBPlatformShellCommand &rhs); @@ -54,6 +55,12 @@ public: void Clear(); + const char * + GetShell(); + + void + SetShell(const char *shell_interpreter); + const char * GetCommand(); diff --git a/lldb/include/lldb/API/SBPlatform.h b/lldb/include/lldb/API/SBPlatform.h index 4d251b129954..98291f18247d 100644 --- a/lldb/include/lldb/API/SBPlatform.h +++ b/lldb/include/lldb/API/SBPlatform.h @@ -51,6 +51,7 @@ class LLDB_API SBPlatformConnectOptions { class LLDB_API SBPlatformShellCommand { public: + SBPlatformShellCommand(const char *shell, const char *shell_command); SBPlatformShellCommand(const char *shell_command); SBPlatformShellCommand(const SBPlatformShellCommand &rhs); @@ -61,6 +62,10 @@ class LLDB_API SBPlatformShellCommand { void Clear(); + const char *GetShell(); + + void SetShell(const char *shell); + const char *GetCommand(); void SetCommand(const char *shell_command); diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h index f19cb85d2329..76792cc6eab5 100644 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -196,19 +196,34 @@ class Host { static Status ShellExpandArguments(ProcessLaunchInfo &launch_info); /// Run a shell command. - /// \arg command shouldn't be NULL + /// \arg command shouldn't be empty /// \arg working_dir Pass empty FileSpec to use the current working directory /// \arg status_ptr Pass NULL if you don't want the process exit status /// \arg signo_ptr Pass NULL if you don't want the signal that caused the /// process to exit /// \arg command_output Pass NULL if you don't want the command output /// \arg hide_stderr if this is false, redirect stderr to stdout - /// TODO: Convert this function to take a StringRef. - static Status RunShellCommand(const char *command, + static Status RunShellCommand(llvm::StringRef command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout<std::micro> &timeout, - bool run_in_default_shell = true, + bool run_in_shell = true, + bool hide_stderr = false); + + /// Run a shell command. + /// \arg shell Pass an empty string if you want to use the default shell + /// interpreter \arg command \arg working_dir Pass empty FileSpec to use the + /// current working directory \arg status_ptr Pass NULL if you don't want + /// the process exit status \arg signo_ptr Pass NULL if you don't want the + /// signal that caused + /// the process to exit + /// \arg command_output Pass NULL if you don't want the command output + /// \arg hide_stderr If this is \b false, redirect stderr to stdout + static Status RunShellCommand(llvm::StringRef shell, llvm::StringRef command, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output, + const Timeout<std::micro> &timeout, + bool run_in_shell = true, bool hide_stderr = false); /// Run a shell command. @@ -222,7 +237,23 @@ class Host { int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout<std::micro> &timeout, - bool run_in_default_shell = true, + bool run_in_shell = true, + bool hide_stderr = false); + + /// Run a shell command. + /// \arg shell Pass an empty string if you want to use the default + /// shell interpreter \arg command \arg working_dir Pass empty FileSpec to use + /// the current working directory \arg status_ptr Pass NULL if you don't + /// want the process exit status \arg signo_ptr Pass NULL if you don't + /// want the signal that caused the + /// process to exit + /// \arg command_output Pass NULL if you don't want the command output + /// \arg hide_stderr If this is \b false, redirect stderr to stdout + static Status RunShellCommand(llvm::StringRef shell, const Args &args, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output, + const Timeout<std::micro> &timeout, + bool run_in_shell = true, bool hide_stderr = false); static bool OpenFileInExternalEditor(const FileSpec &file_spec, diff --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h index 9335f73b37df..64b49ecca606 100644 --- a/lldb/include/lldb/Target/Platform.h +++ b/lldb/include/lldb/Target/Platform.h @@ -621,7 +621,18 @@ class Platform : public PluginInterface { } virtual lldb_private::Status RunShellCommand( - const char *command, // Shouldn't be nullptr + llvm::StringRef command, + const FileSpec &working_dir, // Pass empty FileSpec to use the current + // working directory + int *status_ptr, // Pass nullptr if you don't want the process exit status + int *signo_ptr, // Pass nullptr if you don't want the signal that caused + // the process to exit + std::string + *command_output, // Pass nullptr if you don't want the command output + const Timeout<std::micro> &timeout); + + virtual lldb_private::Status RunShellCommand( + llvm::StringRef shell, llvm::StringRef command, const FileSpec &working_dir, // Pass empty FileSpec to use the current // working directory int *status_ptr, // Pass nullptr if you don't want the process exit status diff --git a/lldb/include/lldb/Target/RemoteAwarePlatform.h b/lldb/include/lldb/Target/RemoteAwarePlatform.h index 5741dbe027b7..6d6ac99c093f 100644 --- a/lldb/include/lldb/Target/RemoteAwarePlatform.h +++ b/lldb/include/lldb/Target/RemoteAwarePlatform.h @@ -68,11 +68,16 @@ class RemoteAwarePlatform : public Platform { bool GetRemoteOSKernelDescription(std::string &s) override; ArchSpec GetRemoteSystemArchitecture() override; - Status RunShellCommand(const char *command, const FileSpec &working_dir, + Status RunShellCommand(llvm::StringRef command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout<std::micro> &timeout) override; + Status RunShellCommand(llvm::StringRef interpreter, llvm::StringRef command, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output, + const Timeout<std::micro> &timeout) override; + const char *GetHostname() override; UserIDResolver &GetUserIDResolver() override; lldb_private::Environment GetEnvironment() override; diff --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp index 7ac852488ffb..3c6422e211fc 100644 --- a/lldb/source/API/SBPlatform.cpp +++ b/lldb/source/API/SBPlatform.cpp @@ -50,14 +50,25 @@ struct PlatformConnectOptions { // PlatformShellCommand struct PlatformShellCommand { - PlatformShellCommand(const char *shell_command = nullptr) + PlatformShellCommand(llvm::StringRef shell_interpreter, + llvm::StringRef shell_command) : m_command(), m_working_dir(), m_status(0), m_signo(0) { - if (shell_command && shell_command[0]) - m_command = shell_command; + if (!shell_interpreter.empty()) + m_shell = shell_interpreter.str(); + + if (!m_shell.empty() && !shell_command.empty()) + m_command = shell_command.str(); + } + + PlatformShellCommand(llvm::StringRef shell_command = llvm::StringRef()) + : m_shell(), m_command(), m_working_dir(), m_status(0), m_signo(0) { + if (!shell_command.empty()) + m_command = shell_command.str(); } ~PlatformShellCommand() = default; + std::string m_shell; std::string m_command; std::string m_working_dir; std::string m_output; @@ -163,6 +174,13 @@ void SBPlatformConnectOptions::SetLocalCacheDirectory(const char *path) { } // SBPlatformShellCommand +SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_interpreter, + const char *shell_command) + : m_opaque_ptr(new PlatformShellCommand(shell_interpreter, shell_command)) { + LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand, (const char *, const char *), + shell_interpreter, shell_command); +} + SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command) : m_opaque_ptr(new PlatformShellCommand(shell_command)) { LLDB_RECORD_CONSTRUCTOR(SBPlatformShellCommand, (const char *), @@ -200,6 +218,24 @@ void SBPlatformShellCommand::Clear() { m_opaque_ptr->m_signo = 0; } +const char *SBPlatformShellCommand::GetShell() { + LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, GetShell); + + if (m_opaque_ptr->m_shell.empty()) + return nullptr; + return m_opaque_ptr->m_shell.c_str(); +} + +void SBPlatformShellCommand::SetShell(const char *shell_interpreter) { + LLDB_RECORD_METHOD(void, SBPlatformShellCommand, SetShell, (const char *), + shell_interpreter); + + if (shell_interpreter && shell_interpreter[0]) + m_opaque_ptr->m_shell = shell_interpreter; + else + m_opaque_ptr->m_shell.clear(); +} + const char *SBPlatformShellCommand::GetCommand() { LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, GetCommand); @@ -557,7 +593,8 @@ SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) { if (working_dir) shell_command.SetWorkingDirectory(working_dir); } - return platform_sp->RunShellCommand(command, FileSpec(working_dir), + return platform_sp->RunShellCommand(shell_command.m_opaque_ptr->m_shell, + command, FileSpec(working_dir), &shell_command.m_opaque_ptr->m_status, &shell_command.m_opaque_ptr->m_signo, &shell_command.m_opaque_ptr->m_output, @@ -699,6 +736,8 @@ void RegisterMethods<SBPlatformShellCommand>(Registry &R) { SBPlatformShellCommand &, SBPlatformShellCommand, operator=,(const lldb::SBPlatformShellCommand &)); LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, Clear, ()); + LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand, GetShell, ()); + LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetShell, (const char *)); LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand, GetCommand, ()); LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetCommand, (const char *)); diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp index b5409e611f05..3a5af9f91cf1 100644 --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -1611,6 +1611,16 @@ class CommandObjectPlatformShell : public CommandObjectRaw { else m_timeout = std::chrono::seconds(timeout_sec); break; + case 's': { + if (option_arg.empty()) { + error.SetErrorStringWithFormat( + "missing shell interpreter path for option -i|--interpreter."); + return error; + } + + m_shell_interpreter = option_arg.str(); + break; + } default: llvm_unreachable("Unimplemented option"); } @@ -1621,10 +1631,12 @@ class CommandObjectPlatformShell : public CommandObjectRaw { void OptionParsingStarting(ExecutionContext *execution_context) override { m_timeout.reset(); m_use_host_platform = false; + m_shell_interpreter.clear(); } Timeout<std::micro> m_timeout = std::chrono::seconds(10); bool m_use_host_platform; + std::string m_shell_interpreter; }; CommandObjectPlatformShell(CommandInterpreter &interpreter) @@ -1650,7 +1662,6 @@ class CommandObjectPlatformShell : public CommandObjectRaw { const bool is_alias = !raw_command_line.contains("platform"); OptionsWithRaw args(raw_command_line); - const char *expr = args.GetRawPart().c_str(); if (args.HasArgs()) if (!ParseOptions(args.GetArgs(), result)) @@ -1662,6 +1673,8 @@ class CommandObjectPlatformShell : public CommandObjectRaw { return false; } + llvm::StringRef cmd = args.GetRawPart(); + PlatformSP platform_sp( m_options.m_use_host_platform ? Platform::GetHostPlatform() @@ -1672,7 +1685,8 @@ class CommandObjectPlatformShell : public CommandObjectRaw { std::string output; int status = -1; int signo = -1; - error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo, + error = (platform_sp->RunShellCommand(m_options.m_shell_interpreter, cmd, + working_dir, &status, &signo, &output, m_options.m_timeout)); if (!output.empty()) result.GetOutputStream().PutCString(output); diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index fbb64957f48d..4bfaf18ec302 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -631,6 +631,8 @@ let Command = "platform shell" in { Desc<"Run the commands on the host shell when enabled.">; def platform_shell_timeout : Option<"timeout", "t">, Arg<"Value">, Desc<"Seconds to wait for the remote host to finish running the command.">; + def platform_shell_interpreter : Option<"shell", "s">, Arg<"Path">, + Desc<"Shell interpreter path. This is the binary used to run the command.">; } let Command = "process attach" in { diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 71c2983ab00f..958fca07850b 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -467,14 +467,24 @@ MonitorShellCommand(std::shared_ptr<ShellInfo> shell_info, lldb::pid_t pid, return true; } -Status Host::RunShellCommand(const char *command, const FileSpec &working_dir, - int *status_ptr, int *signo_ptr, - std::string *command_output_ptr, +Status Host::RunShellCommand(llvm::StringRef command, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output_ptr, + const Timeout<std::micro> &timeout, + bool run_in_shell, bool hide_stderr) { + return RunShellCommand(llvm::StringRef(), Args(command), working_dir, + status_ptr, signo_ptr, command_output_ptr, timeout, + run_in_shell, hide_stderr); +} + +Status Host::RunShellCommand(llvm::StringRef shell_path, + llvm::StringRef command, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output_ptr, const Timeout<std::micro> &timeout, - bool run_in_default_shell, - bool hide_stderr) { - return RunShellCommand(Args(command), working_dir, status_ptr, signo_ptr, - command_output_ptr, timeout, run_in_default_shell, + bool run_in_shell, bool hide_stderr) { + return RunShellCommand(shell_path, Args(command), working_dir, status_ptr, + signo_ptr, command_output_ptr, timeout, run_in_shell, hide_stderr); } @@ -482,14 +492,27 @@ Status Host::RunShellCommand(const Args &args, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output_ptr, const Timeout<std::micro> &timeout, - bool run_in_default_shell, - bool hide_stderr) { + bool run_in_shell, bool hide_stderr) { + return RunShellCommand(llvm::StringRef(), args, working_dir, status_ptr, + signo_ptr, command_output_ptr, timeout, run_in_shell, + hide_stderr); +} + +Status Host::RunShellCommand(llvm::StringRef shell_path, const Args &args, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output_ptr, + const Timeout<std::micro> &timeout, + bool run_in_shell, bool hide_stderr) { Status error; ProcessLaunchInfo launch_info; launch_info.SetArchitecture(HostInfo::GetArchitecture()); - if (run_in_default_shell) { + if (run_in_shell) { // Run the command in a shell - launch_info.SetShell(HostInfo::GetDefaultShell()); + FileSpec shell = HostInfo::GetDefaultShell(); + if (!shell_path.empty()) + shell.SetPath(shell_path); + + launch_info.SetShell(shell); launch_info.GetArguments().AppendArguments(args); const bool localhost = true; const bool will_debug = false; diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm index faac6f59190a..8cd3b3591993 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -1323,11 +1323,11 @@ static bool ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) { launch_info.SetWorkingDirectory(working_dir); } } - bool run_in_default_shell = true; + bool run_in_shell = true; bool hide_stderr = true; - Status e = RunShellCommand(expand_command, cwd, &status, nullptr, &output, - std::chrono::seconds(10), run_in_default_shell, - hide_stderr); + Status e = + RunShellCommand(expand_command, cwd, &status, nullptr, &output, + std::chrono::seconds(10), run_in_shell, hide_stderr); if (e.Fail()) return e; diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index e1eb15c3e8c9..0e0b61f1534f 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -711,7 +711,7 @@ bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) { } Status PlatformRemoteGDBServer::RunShellCommand( - const char *command, // Shouldn't be NULL + llvm::StringRef shell, llvm::StringRef command, const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h index 3562b2bb09df..297b482eb87a 100644 --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -140,7 +140,7 @@ class PlatformRemoteGDBServer : public Platform, private UserIDResolver { Status Unlink(const FileSpec &path) override; Status RunShellCommand( - const char *command, // Shouldn't be NULL + llvm::StringRef shell, llvm::StringRef command, const FileSpec &working_dir, // Pass empty FileSpec to use the current // working directory int *status_ptr, // Pass NULL if you don't want the process exit status diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 0949b9918523..dd0f69841aa7 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2812,7 +2812,7 @@ lldb::addr_t GDBRemoteCommunicationClient::GetShlibInfoAddr() { } lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( - const char *command, // Shouldn't be NULL + llvm::StringRef command, const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status @@ -2823,7 +2823,7 @@ lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( const Timeout<std::micro> &timeout) { lldb_private::StreamString stream; stream.PutCString("qPlatform_shell:"); - stream.PutBytesAsRawHex8(command, strlen(command)); + stream.PutBytesAsRawHex8(command.data(), command.size()); stream.PutChar(','); uint32_t timeout_sec = UINT32_MAX; if (timeout) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 0159125a433b..61acfad5d313 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -399,7 +399,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase { bool GetFileExists(const FileSpec &file_spec); Status RunShellCommand( - const char *command, // Shouldn't be nullptr + llvm::StringRef command, const FileSpec &working_dir, // Pass empty FileSpec to use the current // working directory int *status_ptr, // Pass nullptr if you don't want the process exit status diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index e867b8db4723..7416ea6dd40c 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -1319,7 +1319,23 @@ MmapArgList Platform::GetMmapArgumentList(const ArchSpec &arch, addr_t addr, } lldb_private::Status Platform::RunShellCommand( - const char *command, // Shouldn't be nullptr + llvm::StringRef command, + const FileSpec & + working_dir, // Pass empty FileSpec to use the current working directory + int *status_ptr, // Pass nullptr if you don't want the process exit status + int *signo_ptr, // Pass nullptr if you don't want the signal that caused the + // process to exit + std::string + *command_output, // Pass nullptr if you don't want the command output + const Timeout<std::micro> &timeout) { + return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr, + signo_ptr, command_output, timeout); +} + +lldb_private::Status Platform::RunShellCommand( + llvm::StringRef shell, // Pass empty if you want to use the default + // shell interpreter + llvm::StringRef command, // Shouldn't be empty const FileSpec & working_dir, // Pass empty FileSpec to use the current working directory int *status_ptr, // Pass nullptr if you don't want the process exit status @@ -1329,8 +1345,8 @@ lldb_private::Status Platform::RunShellCommand( *command_output, // Pass nullptr if you don't want the command output const Timeout<std::micro> &timeout) { if (IsHost()) - return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, - command_output, timeout); + return Host::RunShellCommand(shell, command, working_dir, status_ptr, + signo_ptr, command_output, timeout); else return Status("unimplemented"); } diff --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp index f53158b06b8f..3a186adca04c 100644 --- a/lldb/source/Target/RemoteAwarePlatform.cpp +++ b/lldb/source/Target/RemoteAwarePlatform.cpp @@ -171,15 +171,24 @@ Status RemoteAwarePlatform::ResolveExecutable( } Status RemoteAwarePlatform::RunShellCommand( - const char *command, const FileSpec &working_dir, int *status_ptr, + llvm::StringRef command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout<std::micro> &timeout) { + return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr, + signo_ptr, command_output, timeout); +} + +Status RemoteAwarePlatform::RunShellCommand( + llvm::StringRef shell, llvm::StringRef command, const FileSpec &working_dir, + int *status_ptr, int *signo_ptr, std::string *command_output, + const Timeout<std::micro> &timeout) { if (IsHost()) - return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, - command_output, timeout); + return Host::RunShellCommand(shell, command, working_dir, status_ptr, + signo_ptr, command_output, timeout); if (m_remote_platform_sp) - return m_remote_platform_sp->RunShellCommand( - command, working_dir, status_ptr, signo_ptr, command_output, timeout); + return m_remote_platform_sp->RunShellCommand(shell, command, working_dir, + status_ptr, signo_ptr, + command_output, timeout); return Status("unable to run a remote command without a platform"); } diff --git a/lldb/test/API/commands/platform/basic/Makefile b/lldb/test/API/commands/platform/basic/Makefile new file mode 100644 index 000000000000..3626466f607c --- /dev/null +++ b/lldb/test/API/commands/platform/basic/Makefile @@ -0,0 +1,5 @@ +C_SOURCES := myshell.c +CFLAGS_EXTRAS := -g0 # No debug info. +MAKE_DSYM := NO + +include Makefile.rules diff --git a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py index 570f9b3f828d..dc1701258246 100644 --- a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py +++ b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py @@ -13,6 +13,7 @@ class PlatformCommandTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True @no_debug_info_test def test_help_platform(self): @@ -92,3 +93,11 @@ def test_shell_timeout(self): "error: timed out waiting for shell command to complete"]) self.expect("shell -t 1 -- sleep 3", error=True, substrs=[ "error: timed out waiting for shell command to complete"]) + + @no_debug_info_test + def test_host_shell_interpreter(self): + """ Test the host platform shell with a diff erent interpreter """ + self.build() + exe = self.getBuildArtifact('a.out') + self.expect("platform shell -h -s " + exe + " -- 'echo $0'", + substrs=['SUCCESS', 'a.out']) diff --git a/lldb/test/API/commands/platform/basic/TestPlatformPython.py b/lldb/test/API/commands/platform/basic/TestPlatformPython.py index ab10d30b6ff5..0063621e5800 100644 --- a/lldb/test/API/commands/platform/basic/TestPlatformPython.py +++ b/lldb/test/API/commands/platform/basic/TestPlatformPython.py @@ -79,3 +79,20 @@ def test_available_platform_list(self): self.assertEqual( desc_data.GetType(), lldb.eStructuredDataTypeString, 'Platform description is a string') + + @add_test_categories(['pyapi']) + @no_debug_info_test + def test_shell_interpreter(self): + """ Test a shell with a custom interpreter """ + platform = self.dbg.GetSelectedPlatform() + self.assertTrue(platform.IsValid()) + + sh_cmd = lldb.SBPlatformShellCommand('/bin/zsh', 'echo $0') + self.assertIn('/bin/zsh', sh_cmd.GetShell()) + self.assertIn('echo $0', sh_cmd.GetCommand()) + + self.build() + sh_cmd.SetShell(self.getBuildArtifact('a.out')) + err = platform.Run(sh_cmd) + self.assertTrue(err.Success()) + self.assertIn("SUCCESS", sh_cmd.GetOutput()) diff --git a/lldb/test/API/commands/platform/basic/myshell.c b/lldb/test/API/commands/platform/basic/myshell.c new file mode 100644 index 000000000000..d1c0eecb943e --- /dev/null +++ b/lldb/test/API/commands/platform/basic/myshell.c @@ -0,0 +1,24 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "ERROR: Too few arguments (count: %d).\n", argc - 1); + exit(1); + } + +#ifdef WIN32 + char *cmd_opt = "/C"; +#else + char *cmd_opt = "-c"; +#endif + + if (strncmp(argv[1], cmd_opt, 2)) { + fprintf(stderr, "ERROR: Missing shell command option ('%s').\n", cmd_opt); + exit(1); + } + + printf("SUCCESS: %s\n", argv[0]); + return 0; +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits