diff --git a/lldb/bindings/interface/SBPlatform.i b/lldb/bindings/interface/SBPlatform.i --- a/lldb/bindings/interface/SBPlatform.i +++ b/lldb/bindings/interface/SBPlatform.i @@ -45,6 +45,7 @@ class SBPlatformShellCommand { public: + SBPlatformShellCommand (const char *shell_interpreter, const char *shell_command); SBPlatformShellCommand (const char *shell_command); SBPlatformShellCommand (const SBPlatformShellCommand &rhs); @@ -54,6 +55,12 @@ void Clear(); + const char * + GetInterpreter(); + + void + SetInterpreter(const char *shell_interpreter); + const char * GetCommand(); diff --git a/lldb/include/lldb/API/SBPlatform.h b/lldb/include/lldb/API/SBPlatform.h --- a/lldb/include/lldb/API/SBPlatform.h +++ b/lldb/include/lldb/API/SBPlatform.h @@ -51,6 +51,8 @@ class LLDB_API SBPlatformShellCommand { public: + SBPlatformShellCommand(const char *shell_interpreter, + const char *shell_command); SBPlatformShellCommand(const char *shell_command); SBPlatformShellCommand(const SBPlatformShellCommand &rhs); @@ -61,6 +63,10 @@ void Clear(); + const char *GetInterpreter(); + + void SetInterpreter(const char *shell_interpreter); + 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 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -203,12 +203,29 @@ /// 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 &timeout, - bool run_in_default_shell = true, + bool run_in_shell = true, + bool hide_stderr = false); + + /// Run a shell command. + /// \arg interpreter Pass an empty string if you want to use the default + /// shell interpreter + /// \arg command Shouldn't be NUL + /// \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 interpreter, + llvm::StringRef command, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output, + const Timeout &timeout, + bool run_in_shell = true, bool hide_stderr = false); /// Run a shell command. @@ -222,7 +239,23 @@ int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout &timeout, - bool run_in_default_shell = true, + bool run_in_shell = true, + bool hide_stderr = false); + + /// Run a shell command. + /// \arg interpreter Pass an empty string if you want to use the default + /// shell interpreter + /// \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 interpreter, const Args &args, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output, + const Timeout &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 --- a/lldb/include/lldb/Target/Platform.h +++ b/lldb/include/lldb/Target/Platform.h @@ -621,7 +621,7 @@ } 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 @@ -629,7 +629,18 @@ // the process to exit std::string *command_output, // Pass nullptr if you don't want the command output - const Timeout &timeout); + const Timeout &timeout, bool run_in_shell = true); + + virtual lldb_private::Status RunShellCommand( + llvm::StringRef interpreter, 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 &timeout, bool run_in_shell = true); virtual void SetLocalCacheDirectory(const char *local); diff --git a/lldb/include/lldb/Target/RemoteAwarePlatform.h b/lldb/include/lldb/Target/RemoteAwarePlatform.h --- a/lldb/include/lldb/Target/RemoteAwarePlatform.h +++ b/lldb/include/lldb/Target/RemoteAwarePlatform.h @@ -68,10 +68,17 @@ 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 &timeout) override; + const Timeout &timeout, + bool run_in_shell = true) 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 &timeout, + bool run_in_shell = true) override; const char *GetHostname() override; UserIDResolver &GetUserIDResolver() override; diff --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp --- a/lldb/source/API/SBPlatform.cpp +++ b/lldb/source/API/SBPlatform.cpp @@ -50,14 +50,25 @@ // PlatformShellCommand struct PlatformShellCommand { - PlatformShellCommand(const char *shell_command = nullptr) + PlatformShellCommand(llvm::StringRef command_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 (!command_interpreter.empty()) + m_interpreter = command_interpreter.str(); + + if (!m_interpreter.empty() && !shell_command.empty()) + m_command = shell_command.str(); + } + + PlatformShellCommand(llvm::StringRef shell_command = llvm::StringRef()) + : m_interpreter(), 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_interpreter; std::string m_command; std::string m_working_dir; std::string m_output; @@ -163,6 +174,13 @@ } // 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,25 @@ m_opaque_ptr->m_signo = 0; } +const char *SBPlatformShellCommand::GetInterpreter() { + LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, + GetInterpreter); + + if (m_opaque_ptr->m_interpreter.empty()) + return nullptr; + return m_opaque_ptr->m_interpreter.c_str(); +} + +void SBPlatformShellCommand::SetInterpreter(const char *shell_interpreter) { + LLDB_RECORD_METHOD(void, SBPlatformShellCommand, SetInterpreter, + (const char *), shell_interpreter); + + if (shell_interpreter && shell_interpreter[0]) + m_opaque_ptr->m_interpreter = shell_interpreter; + else + m_opaque_ptr->m_interpreter.clear(); +} + const char *SBPlatformShellCommand::GetCommand() { LLDB_RECORD_METHOD_NO_ARGS(const char *, SBPlatformShellCommand, GetCommand); @@ -557,11 +594,12 @@ if (working_dir) shell_command.SetWorkingDirectory(working_dir); } - return platform_sp->RunShellCommand(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, - shell_command.m_opaque_ptr->m_timeout); + return platform_sp->RunShellCommand( + shell_command.m_opaque_ptr->m_interpreter, 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, + shell_command.m_opaque_ptr->m_timeout); })); } @@ -699,6 +737,10 @@ SBPlatformShellCommand &, SBPlatformShellCommand, operator=,(const lldb::SBPlatformShellCommand &)); LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, Clear, ()); + LLDB_REGISTER_METHOD(const char *, SBPlatformShellCommand, GetInterpreter, + ()); + LLDB_REGISTER_METHOD(void, SBPlatformShellCommand, SetInterpreter, + (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 --- a/lldb/source/Commands/CommandObjectPlatform.cpp +++ b/lldb/source/Commands/CommandObjectPlatform.cpp @@ -1611,6 +1611,16 @@ else m_timeout = std::chrono::seconds(timeout_sec); break; + case 'i': { + 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 @@ void OptionParsingStarting(ExecutionContext *execution_context) override { m_timeout.reset(); m_use_host_platform = false; + m_shell_interpreter.clear(); } Timeout m_timeout = std::chrono::seconds(10); bool m_use_host_platform; + std::string m_shell_interpreter; }; CommandObjectPlatformShell(CommandInterpreter &interpreter) @@ -1650,7 +1662,6 @@ 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 @@ return false; } + llvm::StringRef cmd = args.GetRawPart(); + PlatformSP platform_sp( m_options.m_use_host_platform ? Platform::GetHostPlatform() @@ -1672,8 +1685,10 @@ std::string output; int status = -1; int signo = -1; - error = (platform_sp->RunShellCommand(expr, working_dir, &status, &signo, - &output, m_options.m_timeout)); + bool run_in_shell = true; + error = (platform_sp->RunShellCommand( + m_options.m_shell_interpreter, cmd, working_dir, &status, &signo, + &output, m_options.m_timeout, run_in_shell)); if (!output.empty()) result.GetOutputStream().PutCString(output); if (status > 0) { diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -631,6 +631,8 @@ 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<"interpreter", "i">, 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 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -467,14 +467,24 @@ 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 &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 interpreter, + llvm::StringRef command, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output_ptr, const Timeout &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(interpreter, Args(command), working_dir, status_ptr, + signo_ptr, command_output_ptr, timeout, run_in_shell, hide_stderr); } @@ -482,14 +492,27 @@ int *status_ptr, int *signo_ptr, std::string *command_output_ptr, const Timeout &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 interpreter, const Args &args, + const FileSpec &working_dir, int *status_ptr, + int *signo_ptr, std::string *command_output_ptr, + const Timeout &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 (!interpreter.empty()) + shell.SetPath(interpreter); + + 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 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -1323,11 +1323,11 @@ 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.h b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -140,7 +140,8 @@ Status Unlink(const FileSpec &path) override; Status RunShellCommand( - const char *command, // Shouldn't be NULL + llvm::StringRef interpreter, + llvm::StringRef command, // Shouldn't be NULL 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 @@ -148,7 +149,8 @@ // process to exit std::string *command_output, // Pass NULL if you don't want the command output - const lldb_private::Timeout &timeout) override; + const lldb_private::Timeout &timeout, + bool run_in_shell = true) override; void CalculateTrapHandlerSymbolNames() override; diff --git a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp --- a/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -711,7 +711,8 @@ } Status PlatformRemoteGDBServer::RunShellCommand( - const char *command, // Shouldn't be NULL + llvm::StringRef interpreter, + llvm::StringRef command, // Shouldn't be NULL 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 @@ -719,7 +720,7 @@ // process to exit std::string *command_output, // Pass NULL if you don't want the command output - const Timeout &timeout) { + const Timeout &timeout, bool run_in_shell) { return m_gdb_client.RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout); } diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -399,7 +399,7 @@ bool GetFileExists(const FileSpec &file_spec); Status RunShellCommand( - const char *command, // Shouldn't be nullptr + llvm::StringRef command, // Shouldn't be nullptr 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/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2817,7 +2817,7 @@ } lldb_private::Status GDBRemoteCommunicationClient::RunShellCommand( - const char *command, // Shouldn't be NULL + llvm::StringRef command, // Shouldn't be NULL 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 @@ -2828,7 +2828,7 @@ const Timeout &timeout) { lldb_private::StreamString stream; stream.PutCString("qPlatform_shell:"); - stream.PutBytesAsRawHex8(command, strlen(command)); + stream.PutBytesAsRawHex8(command.str().c_str(), command.size()); stream.PutChar(','); uint32_t timeout_sec = UINT32_MAX; if (timeout) { diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -1319,7 +1319,7 @@ } lldb_private::Status Platform::RunShellCommand( - const char *command, // Shouldn't be nullptr + 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 @@ -1327,10 +1327,27 @@ // process to exit std::string *command_output, // Pass nullptr if you don't want the command output - const Timeout &timeout) { + const Timeout &timeout, bool run_in_shell) { + return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr, + signo_ptr, command_output, timeout, run_in_shell); +} + +lldb_private::Status Platform::RunShellCommand( + llvm::StringRef interpreter, // 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 + 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 &timeout, bool run_in_shell) { if (IsHost()) - return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, - command_output, timeout); + return Host::RunShellCommand(interpreter, command, working_dir, status_ptr, + signo_ptr, command_output, timeout, + run_in_shell); else return Status("unimplemented"); } diff --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp --- a/lldb/source/Target/RemoteAwarePlatform.cpp +++ b/lldb/source/Target/RemoteAwarePlatform.cpp @@ -170,16 +170,31 @@ return error; } -Status RemoteAwarePlatform::RunShellCommand( - const char *command, const FileSpec &working_dir, int *status_ptr, - int *signo_ptr, std::string *command_output, - const Timeout &timeout) { +Status RemoteAwarePlatform::RunShellCommand(llvm::StringRef command, + const FileSpec &working_dir, + int *status_ptr, int *signo_ptr, + std::string *command_output, + const Timeout &timeout, + const bool run_in_shell) { + return RunShellCommand(llvm::StringRef(), command, working_dir, status_ptr, + signo_ptr, command_output, timeout, run_in_shell); +} + +Status RemoteAwarePlatform::RunShellCommand(llvm::StringRef interpreter, + llvm::StringRef command, + const FileSpec &working_dir, + int *status_ptr, int *signo_ptr, + std::string *command_output, + const Timeout &timeout, + const bool run_in_shell) { if (IsHost()) - return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, - command_output, timeout); + return Host::RunShellCommand(interpreter, command, working_dir, status_ptr, + signo_ptr, command_output, timeout, + run_in_shell); if (m_remote_platform_sp) return m_remote_platform_sp->RunShellCommand( - command, working_dir, status_ptr, signo_ptr, command_output, timeout); + interpreter, command, working_dir, status_ptr, signo_ptr, + command_output, timeout, run_in_shell); 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 --- /dev/null +++ b/lldb/test/API/commands/platform/basic/Makefile @@ -0,0 +1 @@ +include Makefile.rules diff --git a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py --- a/lldb/test/API/commands/platform/basic/TestPlatformCommand.py +++ b/lldb/test/API/commands/platform/basic/TestPlatformCommand.py @@ -92,3 +92,8 @@ "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 different interpreter """ + self.expect("platform shell -h -i /bin/sh -- 'echo $0'", substrs=['/bin/sh']) diff --git a/lldb/test/API/commands/platform/basic/TestPlatformPython.py b/lldb/test/API/commands/platform/basic/TestPlatformPython.py --- a/lldb/test/API/commands/platform/basic/TestPlatformPython.py +++ b/lldb/test/API/commands/platform/basic/TestPlatformPython.py @@ -79,3 +79,24 @@ 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.GetInterpreter()) + self.assertIn('echo $0', sh_cmd.GetCommand()) + + executable = self.getBuildArtifact('myshell') + d = {'C_SOURCES': 'myshell.c', 'EXE': executable} + self.build(dictionary=d) + self.addTearDownCleanup(dictionary=d) + + sh_cmd.SetInterpreter(executable) + 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 --- /dev/null +++ b/lldb/test/API/commands/platform/basic/myshell.c @@ -0,0 +1,24 @@ +#include +#include +#include + +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; +}