Index: include/lldb/API/SBDebugger.h =================================================================== --- include/lldb/API/SBDebugger.h +++ include/lldb/API/SBDebugger.h @@ -222,6 +222,9 @@ lldb::user_id_t GetID(); + /// Returns the exit code that the user has set from the prompt. + int GetExitCode(); + const char *GetPrompt() const; void SetPrompt(const char *prompt); Index: include/lldb/Core/Debugger.h =================================================================== --- include/lldb/Core/Debugger.h +++ include/lldb/Core/Debugger.h @@ -328,6 +328,10 @@ return m_broadcaster_manager_sp; } + void SetExitCode(int i) { m_exit_code = i; } + + int GetExitCode() const { return m_exit_code; } + protected: friend class CommandInterpreter; friend class REPL; @@ -404,6 +408,8 @@ Broadcaster m_sync_broadcaster; lldb::ListenerSP m_forward_listener_sp; llvm::once_flag m_clear_once; + // The exit code the user has requested for the current debugger process. + int m_exit_code = 0; //---------------------------------------------------------------------- // Events for m_sync_broadcaster Index: lit/Quit/TestQuitExitCode-30.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCode-30.test @@ -0,0 +1,3 @@ +# UNSUPPORTED: windows +# RUN: python %S/expect_exit_code.py 226 %lldb -b -s %s +q -30 Index: lit/Quit/TestQuitExitCode0.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCode0.test @@ -0,0 +1,3 @@ +# UNSUPPORTED: windows +# RUN: %lldb -b -s %s +q 0 Index: lit/Quit/TestQuitExitCode30.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCode30.test @@ -0,0 +1,3 @@ +# UNSUPPORTED: windows +# RUN: python %S/expect_exit_code.py 30 %lldb -b -s %s +q 30 Index: lit/Quit/TestQuitExitCodeHex0.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCodeHex0.test @@ -0,0 +1,3 @@ +# UNSUPPORTED: windows +# RUN: %lldb -b -s %s +q 0x0 Index: lit/Quit/TestQuitExitCodeHexA.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCodeHexA.test @@ -0,0 +1,3 @@ +# UNSUPPORTED: windows +# RUN: python %S/expect_exit_code.py 10 %lldb -b -s %s +q 0xA Index: lit/Quit/TestQuitExitCodeImplicit0.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCodeImplicit0.test @@ -0,0 +1,3 @@ +# UNSUPPORTED: windows +# RUN: %lldb -b -s %s +q Index: lit/Quit/TestQuitExitCodeNonInt.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCodeNonInt.test @@ -0,0 +1,4 @@ +# UNSUPPORTED: windows +# RUN: %lldb -b -s %s 2>&1 | FileCheck %s +q str +// CHECK: Couldn't parse 'str' Index: lit/Quit/TestQuitExitCodeTooManyArgs.test =================================================================== --- /dev/null +++ lit/Quit/TestQuitExitCodeTooManyArgs.test @@ -0,0 +1,4 @@ +# UNSUPPORTED: windows +# RUN: %lldb -b -s %s 2>&1 | FileCheck %s +q 1 2 +// CHECK: Too many arguments for 'quit' Index: lit/Quit/expect_exit_code.py =================================================================== --- /dev/null +++ lit/Quit/expect_exit_code.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python2 + +import subprocess +import sys + +args = sys.argv + +expected_exit_code = args[1] + +args = args[2:] +print("Running " + (" ".join(args))) +real_exit_code = subprocess.call(args) + +if str(real_exit_code) != expected_exit_code: + print("Got exit code %d but expected %s" % (real_exit_code, expected_exit_code)) + exit(1) Index: lit/Quit/lit.local.cfg =================================================================== --- /dev/null +++ lit/Quit/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.test'] Index: source/API/SBDebugger.cpp =================================================================== --- source/API/SBDebugger.cpp +++ source/API/SBDebugger.cpp @@ -1081,6 +1081,10 @@ return (m_opaque_sp ? m_opaque_sp->GetID() : LLDB_INVALID_UID); } +int SBDebugger::GetExitCode() { + return (m_opaque_sp ? m_opaque_sp->GetExitCode() : 0); +} + SBError SBDebugger::SetCurrentPlatform(const char *platform_name_cstr) { SBError sb_error; if (m_opaque_sp) { Index: source/Commands/CommandObjectQuit.cpp =================================================================== --- source/Commands/CommandObjectQuit.cpp +++ source/Commands/CommandObjectQuit.cpp @@ -16,6 +16,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Process.h" +#include "lldb/Utility/StreamString.h" using namespace lldb; using namespace lldb_private; @@ -26,7 +27,7 @@ CommandObjectQuit::CommandObjectQuit(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "quit", "Quit the LLDB debugger.", - "quit") {} + "quit [exit-code]") {} CommandObjectQuit::~CommandObjectQuit() {} @@ -77,6 +78,29 @@ return false; } } + + if (command.GetArgumentCount() > 1) { + result.AppendError("Too many arguments for 'quit'. Only an optional exit " + "code is allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // We parse the exit code argument if there is one. + if (command.GetArgumentCount() == 1) { + llvm::StringRef arg = command.GetArgumentAtIndex(0); + int exit_code; + if (arg.getAsInteger(/*autodetect radix*/ 0, exit_code)) { + lldb_private::StreamString s; + std::string arg_str = arg.str(); + s.Printf("Couldn't parse '%s' as integer for exit code.", arg_str.data()); + result.AppendError(s.GetString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + m_interpreter.GetDebugger().SetExitCode(exit_code); + } + const uint32_t event_type = CommandInterpreter::eBroadcastBitQuitCommandReceived; m_interpreter.BroadcastEvent(event_type); Index: tools/driver/Driver.h =================================================================== --- tools/driver/Driver.h +++ tools/driver/Driver.h @@ -37,7 +37,10 @@ virtual ~Driver(); - void MainLoop(); + /// Runs the main loop. + /// + /// @return The exit code that the process should return. + int MainLoop(); lldb::SBError ParseArgs(int argc, const char *argv[], FILE *out_fh, bool &do_exit); Index: tools/driver/Driver.cpp =================================================================== --- tools/driver/Driver.cpp +++ tools/driver/Driver.cpp @@ -962,7 +962,7 @@ return '"' + arg + '"'; } -void Driver::MainLoop() { +int Driver::MainLoop() { if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) { g_old_stdin_termios_is_valid = true; atexit(reset_stdin_termios); @@ -1159,7 +1159,9 @@ reset_stdin_termios(); fclose(stdin); + int exit_code = m_debugger.GetExitCode(); SBDebugger::Destroy(m_debugger); + return exit_code; } void Driver::ResizeWindow(unsigned short col) { @@ -1237,6 +1239,7 @@ signal(SIGCONT, sigcont_handler); #endif + int exit_code = 0; // Create a scope for driver so that the driver object will destroy itself // before SBDebugger::Terminate() is called. { @@ -1245,14 +1248,15 @@ bool exiting = false; SBError error(driver.ParseArgs(argc, argv, stdout, exiting)); if (error.Fail()) { + exit_code = 1; const char *error_cstr = error.GetCString(); if (error_cstr) ::fprintf(stderr, "error: %s\n", error_cstr); } else if (!exiting) { - driver.MainLoop(); + exit_code = driver.MainLoop(); } } SBDebugger::Terminate(); - return 0; + return exit_code; }