[Lldb-commits] [lldb] r347709 - [Driver] Use libOption with tablegen.

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 27 13:00:32 PST 2018


Author: jdevlieghere
Date: Tue Nov 27 13:00:32 2018
New Revision: 347709

URL: http://llvm.org/viewvc/llvm-project?rev=347709&view=rev
Log:
[Driver] Use libOption with tablegen.

This patch modifies the lldb driver to use libOption for option parsing.
It allows us to decouple option parsing from option processing which is
important when arguments affect initialization. This was previously not
possible because the debugger need to be initialized as some option
interpretation (like the scripting language etc) was handled by the
debugger, rather than in the driver.

Differential revision: https://reviews.llvm.org/D54692

Added:
    lldb/trunk/lit/Driver/
    lldb/trunk/lit/Driver/Inputs/
    lldb/trunk/lit/Driver/Inputs/Print0.in
    lldb/trunk/lit/Driver/Inputs/Print2.in
    lldb/trunk/lit/Driver/Inputs/Print4.in
    lldb/trunk/lit/Driver/Inputs/Print6.in
    lldb/trunk/lit/Driver/TestCommands.test
    lldb/trunk/lit/Driver/TestNoUseColor.test
    lldb/trunk/tools/driver/Options.td
Modified:
    lldb/trunk/lldb.xcodeproj/project.pbxproj
    lldb/trunk/tools/driver/CMakeLists.txt
    lldb/trunk/tools/driver/Driver.cpp
    lldb/trunk/tools/driver/Driver.h

Added: lldb/trunk/lit/Driver/Inputs/Print0.in
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/Driver/Inputs/Print0.in?rev=347709&view=auto
==============================================================================
--- lldb/trunk/lit/Driver/Inputs/Print0.in (added)
+++ lldb/trunk/lit/Driver/Inputs/Print0.in Tue Nov 27 13:00:32 2018
@@ -0,0 +1 @@
+expr 0

Added: lldb/trunk/lit/Driver/Inputs/Print2.in
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/Driver/Inputs/Print2.in?rev=347709&view=auto
==============================================================================
--- lldb/trunk/lit/Driver/Inputs/Print2.in (added)
+++ lldb/trunk/lit/Driver/Inputs/Print2.in Tue Nov 27 13:00:32 2018
@@ -0,0 +1 @@
+expr 2

Added: lldb/trunk/lit/Driver/Inputs/Print4.in
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/Driver/Inputs/Print4.in?rev=347709&view=auto
==============================================================================
--- lldb/trunk/lit/Driver/Inputs/Print4.in (added)
+++ lldb/trunk/lit/Driver/Inputs/Print4.in Tue Nov 27 13:00:32 2018
@@ -0,0 +1 @@
+expr 4

Added: lldb/trunk/lit/Driver/Inputs/Print6.in
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/Driver/Inputs/Print6.in?rev=347709&view=auto
==============================================================================
--- lldb/trunk/lit/Driver/Inputs/Print6.in (added)
+++ lldb/trunk/lit/Driver/Inputs/Print6.in Tue Nov 27 13:00:32 2018
@@ -0,0 +1 @@
+expr 6

Added: lldb/trunk/lit/Driver/TestCommands.test
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/Driver/TestCommands.test?rev=347709&view=auto
==============================================================================
--- lldb/trunk/lit/Driver/TestCommands.test (added)
+++ lldb/trunk/lit/Driver/TestCommands.test Tue Nov 27 13:00:32 2018
@@ -0,0 +1,41 @@
+# RUN: %lldb -x -b \
+# RUN:  -S %S/Inputs/Print0.in \
+# RUN:  -O 'expr 1' \
+# RUN:  -S %S/Inputs/Print2.in \
+# RUN:  -O 'expr 3' \
+# RUN:  -s %S/Inputs/Print4.in \
+# RUN:  -o 'expr 5' \
+# RUN:  -s %S/Inputs/Print6.in \
+# RUN:  -o 'expr 7' \
+# RUN: | FileCheck %s
+#
+# RUN: %lldb -x -b \
+# RUN:  -s %S/Inputs/Print4.in \
+# RUN:  -o 'expr 5' \
+# RUN:  -s %S/Inputs/Print6.in \
+# RUN:  -o 'expr 7' \
+# RUN:  -S %S/Inputs/Print0.in \
+# RUN:  -O 'expr 1' \
+# RUN:  -S %S/Inputs/Print2.in \
+# RUN:  -O 'expr 3' \
+# RUN: | FileCheck %s
+#
+# RUN: %lldb -x -b \
+# RUN:  -s %S/Inputs/Print4.in \
+# RUN:  -S %S/Inputs/Print0.in \
+# RUN:  -o 'expr 5' \
+# RUN:  -O 'expr 1' \
+# RUN:  -s %S/Inputs/Print6.in \
+# RUN:  -S %S/Inputs/Print2.in \
+# RUN:  -o 'expr 7' \
+# RUN:  -O 'expr 3' \
+# RUN: | FileCheck %s
+
+# CHECK: (int) $0 = 0
+# CHECK: (int) $1 = 1
+# CHECK: (int) $2 = 2
+# CHECK: (int) $3 = 3
+# CHECK: (int) $4 = 4
+# CHECK: (int) $5 = 5
+# CHECK: (int) $6 = 6
+# CHECK: (int) $7 = 7

Added: lldb/trunk/lit/Driver/TestNoUseColor.test
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/Driver/TestNoUseColor.test?rev=347709&view=auto
==============================================================================
--- lldb/trunk/lit/Driver/TestNoUseColor.test (added)
+++ lldb/trunk/lit/Driver/TestNoUseColor.test Tue Nov 27 13:00:32 2018
@@ -0,0 +1,4 @@
+# RUN: %lldb --no-use-color -s %s | FileCheck %s
+settings show use-color
+# CHECK: use-color (boolean) = false
+q

Modified: lldb/trunk/lldb.xcodeproj/project.pbxproj
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lldb.xcodeproj/project.pbxproj?rev=347709&r1=347708&r2=347709&view=diff
==============================================================================
--- lldb/trunk/lldb.xcodeproj/project.pbxproj (original)
+++ lldb/trunk/lldb.xcodeproj/project.pbxproj Tue Nov 27 13:00:32 2018
@@ -2373,6 +2373,8 @@
 		260A248D15D06C4F009981B0 /* OptionValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OptionValues.h; path = include/lldb/Interpreter/OptionValues.h; sourceTree = "<group>"; };
 		26BC7E8610F1B85900F91463 /* Options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Options.cpp; path = source/Interpreter/Options.cpp; sourceTree = "<group>"; };
 		26BC7D6D10F1B77400F91463 /* Options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Options.h; path = include/lldb/Interpreter/Options.h; sourceTree = "<group>"; };
+		DD5F951A21ADE5BD00B8265A /* Options.inc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.pascal; name = Options.inc; path = build/Debug/include/Options.inc; sourceTree = "<group>"; };
+		DD5F951B21ADE5F000B8265A /* Options.td */ = {isa = PBXFileReference; lastKnownFileType = text; name = Options.td; path = tools/driver/Options.td; sourceTree = "<group>"; };
 		4C562CC21CC07DDD00C52EAC /* PDBASTParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PDBASTParser.cpp; path = PDB/PDBASTParser.cpp; sourceTree = "<group>"; };
 		4C562CC31CC07DDD00C52EAC /* PDBASTParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PDBASTParser.h; path = PDB/PDBASTParser.h; sourceTree = "<group>"; };
 		4CA0C6CA20F929C600CFE6BB /* PDBLocationToDWARFExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PDBLocationToDWARFExpression.cpp; path = PDB/PDBLocationToDWARFExpression.cpp; sourceTree = "<group>"; };
@@ -5916,6 +5918,8 @@
 		26F5C22510F3D956009D5894 /* Driver */ = {
 			isa = PBXGroup;
 			children = (
+				DD5F951B21ADE5F000B8265A /* Options.td */,
+				DD5F951A21ADE5BD00B8265A /* Options.inc */,
 				26F5C27210F3D9E4009D5894 /* lldb-Info.plist */,
 				26F5C27410F3D9E4009D5894 /* Driver.h */,
 				26F5C27310F3D9E4009D5894 /* Driver.cpp */,
@@ -7173,6 +7177,7 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 26F5C26E10F3D9C5009D5894 /* Build configuration list for PBXNativeTarget "lldb-tool" */;
 			buildPhases = (
+				DD5F951721ADD0C900B8265A /* Run Tablegen */,
 				26F5C26710F3D9A4009D5894 /* Sources */,
 				26F5C26810F3D9A4009D5894 /* Frameworks */,
 			);
@@ -7514,9 +7519,29 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = "/bin/sh -x";
-			shellScript = "/bin/rm -rf \"$INSTALL_ROOT/System/Library/PrivateFrameworks/LLDB.framework/Resources\" \"$INSTALL_ROOT/System/Library/PrivateFrameworks/LLDB.framework/Swift\"";
+			shellScript = "/bin/rm -rf \"$INSTALL_ROOT/System/Library/PrivateFrameworks/LLDB.framework/Resources\" \"$INSTALL_ROOT/System/Library/PrivateFrameworks/LLDB.framework/Swift\"\n";
 			showEnvVarsInLog = 0;
 		};
+		DD5F951721ADD0C900B8265A /* Run Tablegen */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				$SOURCE_DIR/tools/driver/Options.td,
+			);
+			name = "Run Tablegen";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				$BUILT_PRODUCTS_DIR/Options.inc,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "mkdir -p $BUILT_PRODUCTS_DIR/include\n$LLVM_BUILD_DIR/x86_64/bin/llvm-tblgen -I$LLVM_SOURCE_DIR/include -gen-opt-parser-defs $SRCROOT/tools/driver/Options.td -o $BUILT_PRODUCTS_DIR/include/Options.inc\n";
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -10531,7 +10556,7 @@
 					"$(LLVM_BUILD_DIR)/archives.txt",
 				);
 				PRODUCT_NAME = lldb;
-				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include $(SRCROOT)/source $(LLVM_SOURCE_DIR)/include $(LLVM_BUILD_DIR)/$(LLVM_BUILD_DIR_ARCH)/include";
+				USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/include $(SRCROOT)/source $(LLVM_SOURCE_DIR)/include $(LLVM_BUILD_DIR)/$(LLVM_BUILD_DIR_ARCH)/include ${LLDB_BUILD_DIR}/include";
 				VERSIONING_SYSTEM = "apple-generic";
 			};
 			name = Release;

Modified: lldb/trunk/tools/driver/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/CMakeLists.txt?rev=347709&r1=347708&r2=347709&view=diff
==============================================================================
--- lldb/trunk/tools/driver/CMakeLists.txt (original)
+++ lldb/trunk/tools/driver/CMakeLists.txt Tue Nov 27 13:00:32 2018
@@ -1,3 +1,7 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(LLDBOptionsTableGen)
+
 if ((CMAKE_SYSTEM_NAME MATCHES "Windows") OR
     (CMAKE_SYSTEM_NAME MATCHES "NetBSD" ))
   # These targets do not have getopt support, so they rely on the one provided by
@@ -17,6 +21,7 @@ add_lldb_tool(lldb
     ${host_lib}
 
   LINK_COMPONENTS
+    Option
     Support
   )
 
@@ -24,4 +29,8 @@ if ( CMAKE_SYSTEM_NAME MATCHES "Windows"
   add_definitions( -DIMPORT_LIBLLDB )
 endif()
 
-add_dependencies(lldb ${LLDB_SUITE_TARGET})
+add_dependencies(lldb
+  ${LLDB_SUITE_TARGET}
+  LLDBOptionsTableGen
+  ${tablegen_deps}
+)

Modified: lldb/trunk/tools/driver/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Driver.cpp?rev=347709&r1=347708&r2=347709&view=diff
==============================================================================
--- lldb/trunk/tools/driver/Driver.cpp (original)
+++ lldb/trunk/tools/driver/Driver.cpp Tue Nov 27 13:00:32 2018
@@ -9,26 +9,6 @@
 
 #include "Driver.h"
 
-#include <algorithm>
-#include <atomic>
-#include <bitset>
-#include <csignal>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-// Includes for pipe()
-#if defined(_WIN32)
-#include <fcntl.h>
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-
-#include <string>
-
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBCommandInterpreter.h"
 #include "lldb/API/SBCommandReturnObject.h"
@@ -43,18 +23,74 @@
 #include "lldb/API/SBStringList.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBThread.h"
+
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Signals.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <atomic>
+#include <bitset>
+#include <csignal>
+#include <string>
 #include <thread>
 #include <utility>
 
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Includes for pipe()
+#if defined(_WIN32)
+#include <fcntl.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
 #if !defined(__APPLE__)
 #include "llvm/Support/DataTypes.h"
 #endif
 
 using namespace lldb;
+using namespace llvm;
+
+namespace {
+enum ID {
+  OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  {                                                                            \
+      PREFIX,      NAME,      HELPTEXT,                                        \
+      METAVAR,     OPT_##ID,  opt::Option::KIND##Class,                        \
+      PARAM,       FLAGS,     OPT_##GROUP,                                     \
+      OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Options.inc"
+#undef OPTION
+};
+
+class LLDBOptTable : public opt::OptTable {
+public:
+  LLDBOptTable() : OptTable(InfoTable) {}
+};
+} // namespace
 
 static void reset_stdin_termios();
 static bool g_old_stdin_termios_is_valid = false;
@@ -71,110 +107,6 @@ static void reset_stdin_termios() {
   }
 }
 
-typedef struct {
-  uint32_t usage_mask; // Used to mark options that can be used together.  If (1
-                       // << n & usage_mask) != 0
-                       // then this option belongs to option set n.
-  bool required;       // This option is required (in the current usage level)
-  const char *long_option; // Full name for this option.
-  int short_option;        // Single character for this option.
-  int option_has_arg; // no_argument, required_argument or optional_argument
-  uint32_t completion_type; // Cookie the option class can use to do define the
-                            // argument completion.
-  lldb::CommandArgumentType argument_type; // Type of argument this option takes
-  const char *usage_text; // Full text explaining what this options does and
-                          // what (if any) argument to
-                          // pass it.
-} OptionDefinition;
-
-#define LLDB_3_TO_5 LLDB_OPT_SET_3 | LLDB_OPT_SET_4 | LLDB_OPT_SET_5
-#define LLDB_4_TO_5 LLDB_OPT_SET_4 | LLDB_OPT_SET_5
-
-static constexpr OptionDefinition g_options[] = {
-    {LLDB_OPT_SET_1, true, "help", 'h', no_argument, 0, eArgTypeNone,
-     "Prints out the usage information for the LLDB debugger."},
-    {LLDB_OPT_SET_2, true, "version", 'v', no_argument, 0, eArgTypeNone,
-     "Prints out the current version number of the LLDB debugger."},
-    {LLDB_OPT_SET_3, true, "arch", 'a', required_argument, 0,
-     eArgTypeArchitecture,
-     "Tells the debugger to use the specified architecture when starting and "
-     "running the program.  <architecture> must "
-     "be one of the architectures for which the program was compiled."},
-    {LLDB_OPT_SET_3, true, "file", 'f', required_argument, 0, eArgTypeFilename,
-     "Tells the debugger to use the file <filename> as the program to be "
-     "debugged."},
-    {LLDB_OPT_SET_3, false, "core", 'c', required_argument, 0, eArgTypeFilename,
-     "Tells the debugger to use the fullpath to <path> as the core file."},
-    {LLDB_OPT_SET_5, true, "attach-pid", 'p', required_argument, 0, eArgTypePid,
-     "Tells the debugger to attach to a process with the given pid."},
-    {LLDB_OPT_SET_4, true, "attach-name", 'n', required_argument, 0,
-     eArgTypeProcessName,
-     "Tells the debugger to attach to a process with the given name."},
-    {LLDB_OPT_SET_4, true, "wait-for", 'w', no_argument, 0, eArgTypeNone,
-     "Tells the debugger to wait for a process with the given pid or name to "
-     "launch before attaching."},
-    {LLDB_3_TO_5, false, "source", 's', required_argument, 0, eArgTypeFilename,
-     "Tells the debugger to read in and execute the lldb commands in the given "
-     "file, after any file provided on the command line has been loaded."},
-    {LLDB_3_TO_5, false, "one-line", 'o', required_argument, 0, eArgTypeNone,
-     "Tells the debugger to execute this one-line lldb command after any file "
-     "provided on the command line has been loaded."},
-    {LLDB_3_TO_5, false, "source-before-file", 'S', required_argument, 0,
-     eArgTypeFilename,
-     "Tells the debugger to read in and execute the lldb "
-     "commands in the given file, before any file provided "
-     "on the command line has been loaded."},
-    {LLDB_3_TO_5, false, "one-line-before-file", 'O', required_argument, 0,
-     eArgTypeNone,
-     "Tells the debugger to execute this one-line lldb command "
-     "before any file provided on the command line has been "
-     "loaded."},
-    {LLDB_3_TO_5, false, "one-line-on-crash", 'k', required_argument, 0,
-     eArgTypeNone,
-     "When in batch mode, tells the debugger to execute this "
-     "one-line lldb command if the target crashes."},
-    {LLDB_3_TO_5, false, "source-on-crash", 'K', required_argument, 0,
-     eArgTypeFilename,
-     "When in batch mode, tells the debugger to source this "
-     "file of lldb commands if the target crashes."},
-    {LLDB_3_TO_5, false, "source-quietly", 'Q', no_argument, 0, eArgTypeNone,
-     "Tells the debugger to execute this one-line lldb command before any file "
-     "provided on the command line has been loaded."},
-    {LLDB_3_TO_5, false, "batch", 'b', no_argument, 0, eArgTypeNone,
-     "Tells the debugger to run the commands from -s, -S, -o & -O, and "
-     "then quit.  However if any run command stopped due to a signal or crash, "
-     "the debugger will return to the interactive prompt at the place of the "
-     "crash."},
-    {LLDB_3_TO_5, false, "editor", 'e', no_argument, 0, eArgTypeNone,
-     "Tells the debugger to open source files using the host's \"external "
-     "editor\" mechanism."},
-    {LLDB_3_TO_5, false, "no-lldbinit", 'x', no_argument, 0, eArgTypeNone,
-     "Do not automatically parse any '.lldbinit' files."},
-    {LLDB_3_TO_5, false, "no-use-colors", 'X', no_argument, 0, eArgTypeNone,
-     "Do not use colors."},
-    {LLDB_OPT_SET_6, true, "python-path", 'P', no_argument, 0, eArgTypeNone,
-     "Prints out the path to the lldb.py file for this version of lldb."},
-    {LLDB_3_TO_5, false, "script-language", 'l', required_argument, 0,
-     eArgTypeScriptLang,
-     "Tells the debugger to use the specified scripting language for "
-     "user-defined scripts, rather than the default.  "
-     "Valid scripting languages that can be specified include Python, Perl, "
-     "Ruby and Tcl.  Currently only the Python "
-     "extensions have been implemented."},
-    {LLDB_3_TO_5, false, "debug", 'd', no_argument, 0, eArgTypeNone,
-     "Tells the debugger to print out extra information for debugging itself."},
-    {LLDB_3_TO_5, false, "reproducer", 'z', required_argument, 0,
-     eArgTypeFilename,
-     "Tells the debugger to use the fullpath to <path> as a reproducer."},
-    {LLDB_OPT_SET_7, true, "repl", 'r', optional_argument, 0, eArgTypeNone,
-     "Runs lldb in REPL mode with a stub process."},
-    {LLDB_OPT_SET_7, true, "repl-language", 'R', required_argument, 0,
-     eArgTypeNone, "Chooses the language for the REPL."}};
-
-static constexpr auto g_num_options = sizeof(g_options)/sizeof(OptionDefinition);
-
-static const uint32_t last_option_set_with_args = 2;
-
 Driver::Driver()
     : SBBroadcaster("Driver"), m_debugger(SBDebugger::Create(false)),
       m_option_data() {
@@ -186,241 +118,14 @@ Driver::Driver()
 
 Driver::~Driver() { g_driver = NULL; }
 
-// This function takes INDENT, which tells how many spaces to output at the
-// front
-// of each line; TEXT, which is the text that is to be output. It outputs the
-// text, on multiple lines if necessary, to RESULT, with INDENT spaces at the
-// front of each line.  It breaks lines on spaces, tabs or newlines, shortening
-// the line if necessary to not break in the middle of a word. It assumes that
-// each output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.
-
-void OutputFormattedUsageText(FILE *out, int indent, const char *text,
-                              int output_max_columns) {
-  int len = strlen(text);
-  std::string text_string(text);
-
-  // Force indentation to be reasonable.
-  if (indent >= output_max_columns)
-    indent = 0;
-
-  // Will it all fit on one line?
-
-  if (len + indent < output_max_columns)
-    // Output as a single line
-    fprintf(out, "%*s%s\n", indent, "", text);
-  else {
-    // We need to break it up into multiple lines.
-    int text_width = output_max_columns - indent - 1;
-    int start = 0;
-    int end = start;
-    int final_end = len;
-    int sub_len;
-
-    while (end < final_end) {
-      // Dont start the 'text' on a space, since we're already outputting the
-      // indentation.
-      while ((start < final_end) && (text[start] == ' '))
-        start++;
-
-      end = start + text_width;
-      if (end > final_end)
-        end = final_end;
-      else {
-        // If we're not at the end of the text, make sure we break the line on
-        // white space.
-        while (end > start && text[end] != ' ' && text[end] != '\t' &&
-               text[end] != '\n')
-          end--;
-      }
-      sub_len = end - start;
-      std::string substring = text_string.substr(start, sub_len);
-      fprintf(out, "%*s%s\n", indent, "", substring.c_str());
-      start = end + 1;
-    }
-  }
-}
-
-static void ShowUsage(FILE *out, Driver::OptionData data) {
-  uint32_t screen_width = 80;
-  uint32_t indent_level = 0;
-  const char *name = "lldb";
-
-  fprintf(out, "\nUsage:\n\n");
-
-  indent_level += 2;
-
-  // First, show each usage level set of options, e.g. <cmd>
-  // [options-for-level-0]
-  //                                                   <cmd>
-  //                                                   [options-for-level-1]
-  //                                                   etc.
-
-  uint32_t num_option_sets = 0;
-
-  for (const auto &opt : g_options) {
-    uint32_t this_usage_mask = opt.usage_mask;
-    if (this_usage_mask == LLDB_OPT_SET_ALL) {
-      if (num_option_sets == 0)
-        num_option_sets = 1;
-    } else {
-      for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) {
-        if (this_usage_mask & 1 << j) {
-          if (num_option_sets <= j)
-            num_option_sets = j + 1;
-        }
-      }
-    }
-  }
-
-  for (uint32_t opt_set = 0; opt_set < num_option_sets; opt_set++) {
-    uint32_t opt_set_mask;
-
-    opt_set_mask = 1 << opt_set;
-
-    if (opt_set > 0)
-      fprintf(out, "\n");
-    fprintf(out, "%*s%s", indent_level, "", name);
-    bool is_help_line = false;
-
-    for (const auto &opt : g_options) {
-      if (opt.usage_mask & opt_set_mask) {
-        CommandArgumentType arg_type = opt.argument_type;
-        const char *arg_name =
-            SBCommandInterpreter::GetArgumentTypeAsCString(arg_type);
-        // This is a bit of a hack, but there's no way to say certain options
-        // don't have arguments yet...
-        // so we do it by hand here.
-        if (opt.short_option == 'h')
-          is_help_line = true;
-
-        if (opt.required) {
-          if (opt.option_has_arg == required_argument)
-            fprintf(out, " -%c <%s>", opt.short_option, arg_name);
-          else if (opt.option_has_arg == optional_argument)
-            fprintf(out, " -%c [<%s>]", opt.short_option, arg_name);
-          else
-            fprintf(out, " -%c", opt.short_option);
-        } else {
-          if (opt.option_has_arg == required_argument)
-            fprintf(out, " [-%c <%s>]", opt.short_option, arg_name);
-          else if (opt.option_has_arg == optional_argument)
-            fprintf(out, " [-%c [<%s>]]", opt.short_option,
-                    arg_name);
-          else
-            fprintf(out, " [-%c]", opt.short_option);
-        }
-      }
-    }
-    if (!is_help_line && (opt_set <= last_option_set_with_args))
-      fprintf(out, " [[--] <PROGRAM-ARG-1> [<PROGRAM_ARG-2> ...]]");
-  }
-
-  fprintf(out, "\n\n");
-
-  // Now print out all the detailed information about the various options:  long
-  // form, short form and help text:
-  //   -- long_name <argument>
-  //   - short <argument>
-  //   help text
-
-  // This variable is used to keep track of which options' info we've printed
-  // out, because some options can be in
-  // more than one usage level, but we only want to print the long form of its
-  // information once.
-
-  Driver::OptionData::OptionSet options_seen;
-  Driver::OptionData::OptionSet::iterator pos;
-
-  indent_level += 5;
-
-  for (const auto &opt : g_options) {
-    // Only print this option if we haven't already seen it.
-    pos = options_seen.find(opt.short_option);
-    if (pos == options_seen.end()) {
-      CommandArgumentType arg_type = opt.argument_type;
-      const char *arg_name =
-          SBCommandInterpreter::GetArgumentTypeAsCString(arg_type);
-
-      options_seen.insert(opt.short_option);
-      fprintf(out, "%*s-%c ", indent_level, "", opt.short_option);
-      if (arg_type != eArgTypeNone)
-        fprintf(out, "<%s>", arg_name);
-      fprintf(out, "\n");
-      fprintf(out, "%*s--%s ", indent_level, "", opt.long_option);
-      if (arg_type != eArgTypeNone)
-        fprintf(out, "<%s>", arg_name);
-      fprintf(out, "\n");
-      indent_level += 5;
-      OutputFormattedUsageText(out, indent_level, opt.usage_text,
-                               screen_width);
-      indent_level -= 5;
-      fprintf(out, "\n");
-    }
-  }
-
-  indent_level -= 5;
-
-  fprintf(out, "\n%*sNotes:\n", indent_level, "");
-  indent_level += 5;
-
-  fprintf(out,
-          "\n%*sMultiple \"-s\" and \"-o\" options can be provided.  They will "
-          "be processed"
-          "\n%*sfrom left to right in order, with the source files and commands"
-          "\n%*sinterleaved.  The same is true of the \"-S\" and \"-O\" "
-          "options.  The before"
-          "\n%*sfile and after file sets can intermixed freely, the command "
-          "parser will"
-          "\n%*ssort them out.  The order of the file specifiers (\"-c\", "
-          "\"-f\", etc.) is"
-          "\n%*snot significant in this regard.\n\n",
-          indent_level, "", indent_level, "", indent_level, "", indent_level,
-          "", indent_level, "", indent_level, "");
-
-  fprintf(
-      out,
-      "\n%*sIf you don't provide -f then the first argument will be the file "
-      "to be"
-      "\n%*sdebugged which means that '%s -- <filename> [<ARG1> [<ARG2>]]' also"
-      "\n%*sworks.  But remember to end the options with \"--\" if any of your"
-      "\n%*sarguments have a \"-\" in them.\n\n",
-      indent_level, "", indent_level, "", name, indent_level, "", indent_level,
-      "");
-}
-
- static void BuildGetOptTable(std::vector<option> &getopt_table) {
-  getopt_table.resize(g_num_options + 1);
-
-  std::bitset<256> option_seen;
-  uint32_t j = 0;
-  for (const auto &opt : g_options) {
-    char short_opt = opt.short_option;
-
-    if (option_seen.test(short_opt) == false) {
-      getopt_table[j].name = opt.long_option;
-      getopt_table[j].has_arg = opt.option_has_arg;
-      getopt_table[j].flag = NULL;
-      getopt_table[j].val = opt.short_option;
-      option_seen.set(short_opt);
-      ++j;
-    }
-  }
-
-  getopt_table[j].name = NULL;
-  getopt_table[j].has_arg = 0;
-  getopt_table[j].flag = NULL;
-  getopt_table[j].val = 0;
-}
-
 Driver::OptionData::OptionData()
     : m_args(), m_script_lang(lldb::eScriptLanguageDefault), m_core_file(),
       m_crash_log(), m_initial_commands(), m_after_file_commands(),
       m_after_crash_commands(), m_debug_mode(false), m_source_quietly(false),
-      m_print_version(false), m_print_python_path(false), m_print_help(false),
-      m_wait_for(false), m_repl(false), m_repl_lang(eLanguageTypeUnknown),
-      m_repl_options(), m_process_name(),
-      m_process_pid(LLDB_INVALID_PROCESS_ID), m_use_external_editor(false),
-      m_batch(false), m_seen_options() {}
+      m_print_version(false), m_print_python_path(false), m_wait_for(false),
+      m_repl(false), m_repl_lang(eLanguageTypeUnknown), m_repl_options(),
+      m_process_name(), m_process_pid(LLDB_INVALID_PROCESS_ID),
+      m_use_external_editor(false), m_batch(false), m_seen_options() {}
 
 Driver::OptionData::~OptionData() {}
 
@@ -441,9 +146,8 @@ void Driver::OptionData::Clear() {
   // Only read .lldbinit in the current working directory
   // if it's not the same as the .lldbinit in the home
   // directory (which is already being read in).
-  if (local_lldbinit.Exists() &&
-      strcmp(local_lldbinit.GetDirectory(), homedir_dot_lldb.GetDirectory()) !=
-          0) {
+  if (local_lldbinit.Exists() && strcmp(local_lldbinit.GetDirectory(),
+                                        homedir_dot_lldb.GetDirectory()) != 0) {
     char path[2048];
     local_lldbinit.GetPath(path, 2047);
     InitialCmdEntry entry(path, true, true, true);
@@ -452,7 +156,6 @@ void Driver::OptionData::Clear() {
 
   m_debug_mode = false;
   m_source_quietly = false;
-  m_print_help = false;
   m_print_version = false;
   m_print_python_path = false;
   m_use_external_editor = false;
@@ -464,7 +167,7 @@ void Driver::OptionData::Clear() {
   m_process_pid = LLDB_INVALID_PROCESS_ID;
 }
 
-void Driver::OptionData::AddInitialCommand(const char *command,
+void Driver::OptionData::AddInitialCommand(std::string command,
                                            CommandPlacement placement,
                                            bool is_file, SBError &error) {
   std::vector<InitialCmdEntry> *command_set;
@@ -481,7 +184,7 @@ void Driver::OptionData::AddInitialComma
   }
 
   if (is_file) {
-    SBFileSpec file(command);
+    SBFileSpec file(command.c_str());
     if (file.Exists())
       command_set->push_back(InitialCmdEntry(command, is_file, false));
     else if (file.ResolveExecutableLocation()) {
@@ -490,7 +193,8 @@ void Driver::OptionData::AddInitialComma
       command_set->push_back(InitialCmdEntry(final_path, is_file, false));
     } else
       error.SetErrorStringWithFormat(
-          "file specified in --source (-s) option doesn't exist: '%s'", optarg);
+          "file specified in --source (-s) option doesn't exist: '%s'",
+          command.c_str());
   } else
     command_set->push_back(InitialCmdEntry(command, is_file, false));
 }
@@ -572,259 +276,239 @@ void Driver::WriteCommandsForSourcing(Co
 bool Driver::GetDebugMode() const { return m_option_data.m_debug_mode; }
 
 // Check the arguments that were passed to this program to make sure they are
-// valid and to get their
-// argument values (if any).  Return a boolean value indicating whether or not
-// to start up the full
-// debugger (i.e. the Command Interpreter) or not.  Return FALSE if the
-// arguments were invalid OR
-// if the user only wanted help or version information.
-
-SBError Driver::ParseArgs(int argc, const char *argv[], FILE *out_fh,
-                          bool &exiting) {
-  static_assert(g_num_options > 0, "cannot handle arguments");
-
+// valid and to get their argument values (if any).  Return a boolean value
+// indicating whether or not to start up the full debugger (i.e. the Command
+// Interpreter) or not.  Return FALSE if the arguments were invalid OR if the
+// user only wanted help or version information.
+SBError Driver::ProcessArgs(const opt::InputArgList &args, FILE *out_fh,
+                            bool &exiting) {
+  SBError error;
   ResetOptionValues();
 
-  SBError error;
-  std::vector<option> long_options_vector;
-  BuildGetOptTable(long_options_vector);
-  if (long_options_vector.empty()) {
-    error.SetErrorStringWithFormat("invalid long options");
-    return error;
+  // This is kind of a pain, but since we make the debugger in the Driver's
+  // constructor, we can't know at that point whether we should read in init
+  // files yet.  So we don't read them in in the Driver constructor, then set
+  // the flags back to "read them in" here, and then if we see the "-n" flag,
+  // we'll turn it off again.  Finally we have to read them in by hand later in
+  // the main loop.
+  m_debugger.SkipLLDBInitFiles(false);
+  m_debugger.SkipAppInitFiles(false);
+
+  if (args.hasArg(OPT_version)) {
+    m_option_data.m_print_version = true;
   }
 
-  // Build the option_string argument for call to getopt_long_only.
-  std::string option_string;
-  auto sentinel_it = std::prev(std::end(long_options_vector));
-  for (auto long_opt_it = std::begin(long_options_vector);
-            long_opt_it != sentinel_it; ++long_opt_it) {
-    if (long_opt_it->flag == nullptr) {
-      option_string.push_back(static_cast<char>(long_opt_it->val));
-      switch (long_opt_it->has_arg) {
-      default:
-      case no_argument:
-        break;
-      case required_argument:
-        option_string.push_back(':');
-        break;
-      case optional_argument:
-        option_string.append("::");
-        break;
-      }
+  if (args.hasArg(OPT_python_path)) {
+    m_option_data.m_print_python_path = true;
+  }
+
+  if (args.hasArg(OPT_batch)) {
+    m_option_data.m_batch = true;
+  }
+
+  if (args.hasArg(OPT_core)) {
+    SBFileSpec file(optarg);
+    if (file.Exists()) {
+      m_option_data.m_core_file = optarg;
+    } else {
+      error.SetErrorStringWithFormat(
+          "file specified in --core (-c) option doesn't exist: '%s'", optarg);
+      return error;
     }
   }
 
-  // This is kind of a pain, but since we make the debugger in the Driver's
-  // constructor, we can't
-  // know at that point whether we should read in init files yet.  So we don't
-  // read them in in the
-  // Driver constructor, then set the flags back to "read them in" here, and
-  // then if we see the
-  // "-n" flag, we'll turn it off again.  Finally we have to read them in by
-  // hand later in the
-  // main loop.
+  if (args.hasArg(OPT_editor)) {
+    m_option_data.m_use_external_editor = true;
+  }
 
-  m_debugger.SkipLLDBInitFiles(false);
-  m_debugger.SkipAppInitFiles(false);
+  if (args.hasArg(OPT_no_lldbinit)) {
+    m_debugger.SkipLLDBInitFiles(true);
+    m_debugger.SkipAppInitFiles(true);
+  }
 
-// Prepare for & make calls to getopt_long_only.
-#if __GLIBC__
-  optind = 0;
-#else
-  optreset = 1;
-  optind = 1;
-#endif
-  int val;
-  while (1) {
-    int long_options_index = -1;
-    val = ::getopt_long_only(argc, const_cast<char **>(argv),
-                             option_string.c_str(), long_options_vector.data(),
-                             &long_options_index);
-
-    if (val == -1)
-      break;
-    else if (val == '?') {
-      m_option_data.m_print_help = true;
-      error.SetErrorStringWithFormat("unknown or ambiguous option");
-      break;
-    } else if (val == 0)
-      continue;
-    else {
-      m_option_data.m_seen_options.insert((char)val);
-      if (long_options_index == -1) {
-        auto long_opt_it = std::find_if(std::begin(long_options_vector), sentinel_it,
-            [val](const option &long_option) { return long_option.val == val; });
-        if (std::end(long_options_vector) != long_opt_it)
-          long_options_index =
-              std::distance(std::begin(long_options_vector), long_opt_it);
-      }
+  if (args.hasArg(OPT_no_use_colors)) {
+    m_debugger.SetUseColor(false);
+  }
 
-      if (long_options_index >= 0) {
-        const int short_option = g_options[long_options_index].short_option;
+  if (auto *arg = args.getLastArg(OPT_file)) {
+    auto optarg = arg->getValue();
+    SBFileSpec file(optarg);
+    if (file.Exists()) {
+      m_option_data.m_args.push_back(optarg);
+    } else if (file.ResolveExecutableLocation()) {
+      char path[PATH_MAX];
+      file.GetPath(path, sizeof(path));
+      m_option_data.m_args.push_back(path);
+    } else {
+      error.SetErrorStringWithFormat(
+          "file specified in --file (-f) option doesn't exist: '%s'", optarg);
+      return error;
+    }
+  }
 
-        switch (short_option) {
-        case 'h':
-          m_option_data.m_print_help = true;
-          break;
-
-        case 'v':
-          m_option_data.m_print_version = true;
-          break;
-
-        case 'P':
-          m_option_data.m_print_python_path = true;
-          break;
-
-        case 'b':
-          m_option_data.m_batch = true;
-          break;
-
-        case 'c': {
-          SBFileSpec file(optarg);
-          if (file.Exists()) {
-            m_option_data.m_core_file = optarg;
-          } else
-            error.SetErrorStringWithFormat(
-                "file specified in --core (-c) option doesn't exist: '%s'",
-                optarg);
-        } break;
-
-        case 'e':
-          m_option_data.m_use_external_editor = true;
-          break;
-
-        case 'x':
-          m_debugger.SkipLLDBInitFiles(true);
-          m_debugger.SkipAppInitFiles(true);
-          break;
-
-        case 'X':
-          m_debugger.SetUseColor(false);
-          break;
-
-        case 'f': {
-          SBFileSpec file(optarg);
-          if (file.Exists()) {
-            m_option_data.m_args.push_back(optarg);
-          } else if (file.ResolveExecutableLocation()) {
-            char path[PATH_MAX];
-            file.GetPath(path, sizeof(path));
-            m_option_data.m_args.push_back(path);
-          } else
-            error.SetErrorStringWithFormat(
-                "file specified in --file (-f) option doesn't exist: '%s'",
-                optarg);
-        } break;
-
-        case 'a':
-          if (!m_debugger.SetDefaultArchitecture(optarg))
-            error.SetErrorStringWithFormat(
-                "invalid architecture in the -a or --arch option: '%s'",
-                optarg);
-          break;
-
-        case 'l':
-          m_option_data.m_script_lang = m_debugger.GetScriptingLanguage(optarg);
-          break;
-
-        case 'd':
-          m_option_data.m_debug_mode = true;
-          break;
-
-        case 'z': {
-          SBFileSpec file(optarg);
-          if (file.Exists()) {
-            m_debugger.SetReproducerPath(optarg);
-          } else
-            error.SetErrorStringWithFormat("file specified in --reproducer "
-                                           "(-z) option doesn't exist: '%s'",
-                                           optarg);
-        } break;
-
-        case 'Q':
-          m_option_data.m_source_quietly = true;
-          break;
-
-        case 'K':
-          m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash,
-                                          true, error);
-          break;
-        case 'k':
-          m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash,
-                                          false, error);
-          break;
-
-        case 'n':
-          m_option_data.m_process_name = optarg;
-          break;
-
-        case 'w':
-          m_option_data.m_wait_for = true;
-          break;
-
-        case 'p': {
-          char *remainder;
-          m_option_data.m_process_pid = strtol(optarg, &remainder, 0);
-          if (remainder == optarg || *remainder != '\0')
-            error.SetErrorStringWithFormat(
-                "Could not convert process PID: \"%s\" into a pid.", optarg);
-        } break;
-
-        case 'r':
-          m_option_data.m_repl = true;
-          if (optarg && optarg[0])
-            m_option_data.m_repl_options = optarg;
-          else
-            m_option_data.m_repl_options.clear();
-          break;
-
-        case 'R':
-          m_option_data.m_repl_lang =
-              SBLanguageRuntime::GetLanguageTypeFromString(optarg);
-          if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {
-            error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",
-                                           optarg);
-          }
-          break;
+  if (auto *arg = args.getLastArg(OPT_arch)) {
+    auto optarg = arg->getValue();
+    if (!m_debugger.SetDefaultArchitecture(optarg)) {
+      error.SetErrorStringWithFormat(
+          "invalid architecture in the -a or --arch option: '%s'", optarg);
+      return error;
+    }
+  }
 
-        case 's':
-          m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile,
-                                          true, error);
-          break;
-        case 'o':
-          m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile,
-                                          false, error);
-          break;
-        case 'S':
-          m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile,
-                                          true, error);
-          break;
-        case 'O':
-          m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile,
-                                          false, error);
-          break;
-        default:
-          m_option_data.m_print_help = true;
-          error.SetErrorStringWithFormat("unrecognized option %c",
-                                         short_option);
-          break;
-        }
-      } else {
-        error.SetErrorStringWithFormat("invalid option with value %i", val);
-      }
-      if (error.Fail()) {
+  if (auto *arg = args.getLastArg(OPT_script_language)) {
+    auto optarg = arg->getValue();
+    m_option_data.m_script_lang = m_debugger.GetScriptingLanguage(optarg);
+  }
+
+  if (args.hasArg(OPT_no_use_colors)) {
+    m_option_data.m_debug_mode = true;
+  }
+
+  if (auto *arg = args.getLastArg(OPT_reproducer)) {
+    auto optarg = arg->getValue();
+    SBFileSpec file(optarg);
+    if (file.Exists()) {
+      m_debugger.SetReproducerPath(optarg);
+    } else {
+      error.SetErrorStringWithFormat("file specified in --reproducer "
+                                     "(-z) option doesn't exist: '%s'",
+                                     optarg);
+      return error;
+    }
+  }
+
+  if (args.hasArg(OPT_no_use_colors)) {
+    m_debugger.SetUseColor(false);
+  }
+
+  if (args.hasArg(OPT_source_quietly)) {
+    m_option_data.m_source_quietly = true;
+  }
+
+  if (auto *arg = args.getLastArg(OPT_attach_name)) {
+    auto optarg = arg->getValue();
+    m_option_data.m_process_name = optarg;
+  }
+
+  if (args.hasArg(OPT_wait_for)) {
+    m_option_data.m_wait_for = true;
+  }
+
+  if (auto *arg = args.getLastArg(OPT_attach_pid)) {
+    auto optarg = arg->getValue();
+    char *remainder;
+    m_option_data.m_process_pid = strtol(optarg, &remainder, 0);
+    if (remainder == optarg || *remainder != '\0') {
+      error.SetErrorStringWithFormat(
+          "Could not convert process PID: \"%s\" into a pid.", optarg);
+      return error;
+    }
+  }
+
+  if (auto *arg = args.getLastArg(OPT_repl_language)) {
+    auto optarg = arg->getValue();
+    m_option_data.m_repl_lang =
+        SBLanguageRuntime::GetLanguageTypeFromString(optarg);
+    if (m_option_data.m_repl_lang == eLanguageTypeUnknown) {
+      error.SetErrorStringWithFormat("Unrecognized language name: \"%s\"",
+                                     optarg);
+      return error;
+    }
+  }
+
+  if (auto *arg = args.getLastArg(OPT_repl)) {
+    auto optarg = arg->getValue();
+    m_option_data.m_repl = true;
+    if (optarg && optarg[0])
+      m_option_data.m_repl_options = optarg;
+    else
+      m_option_data.m_repl_options.clear();
+  }
+
+  // We need to process the options below together as their relative order
+  // matters.
+  for (auto *arg : args.filtered(OPT_source_on_crash, OPT_one_line_on_crash,
+                                 OPT_source, OPT_source_before_file,
+                                 OPT_one_line, OPT_one_line_before_file)) {
+    if (arg->getOption().matches(OPT_source_on_crash)) {
+      auto optarg = arg->getValue();
+      m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash, true,
+                                      error);
+      if (error.Fail())
         return error;
+    }
+
+    if (arg->getOption().matches(OPT_one_line_on_crash)) {
+      auto optarg = arg->getValue();
+      m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash,
+                                      false, error);
+      if (error.Fail())
+        return error;
+    }
+
+    if (arg->getOption().matches(OPT_source)) {
+      auto optarg = arg->getValue();
+      m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, true,
+                                      error);
+      if (error.Fail())
+        return error;
+    }
+
+    if (arg->getOption().matches(OPT_source_before_file)) {
+      auto optarg = arg->getValue();
+      m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile, true,
+                                      error);
+      if (error.Fail())
+        return error;
+    }
+
+    if (arg->getOption().matches(OPT_one_line)) {
+      auto optarg = arg->getValue();
+      m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, false,
+                                      error);
+      if (error.Fail())
+        return error;
+    }
+
+    if (arg->getOption().matches(OPT_one_line_before_file)) {
+      auto optarg = arg->getValue();
+      m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile,
+                                      false, error);
+      if (error.Fail())
+        return error;
+    }
+  }
+
+  if (m_option_data.m_process_name.empty() &&
+      m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) {
+
+    // If the option data args array is empty that means the file was not
+    // specified with -f and we need to get it from the input args.
+    if (m_option_data.m_args.empty()) {
+      if (auto *arg = args.getLastArgNoClaim(OPT_INPUT)) {
+        m_option_data.m_args.push_back(arg->getAsString((args)));
       }
     }
+
+    // Any argument following -- is an argument for the inferior.
+    if (auto *arg = args.getLastArgNoClaim(OPT_REM)) {
+      for (auto value : arg->getValues())
+        m_option_data.m_args.push_back(value);
+    }
+  } else {
+    if (args.getLastArgNoClaim()) {
+      ::fprintf(out_fh,
+                "Warning: program arguments are ignored when attaching.\n");
+    }
   }
 
-  if (error.Fail() || m_option_data.m_print_help) {
-    ShowUsage(out_fh, m_option_data);
-    exiting = true;
-  } else if (m_option_data.m_print_version) {
+  if (m_option_data.m_print_version) {
     ::fprintf(out_fh, "%s\n", m_debugger.GetVersionString());
     exiting = true;
-  } else if (m_option_data.m_print_python_path) {
+    return error;
+  }
+
+  if (m_option_data.m_print_python_path) {
     SBFileSpec python_file_spec = SBHostOS::GetLLDBPythonPath();
     if (python_file_spec.IsValid()) {
       char python_path[PATH_MAX];
@@ -836,33 +520,7 @@ SBError Driver::ParseArgs(int argc, cons
     } else
       ::fprintf(out_fh, "<COULD NOT FIND PATH>\n");
     exiting = true;
-  } else if (m_option_data.m_process_name.empty() &&
-             m_option_data.m_process_pid == LLDB_INVALID_PROCESS_ID) {
-    // Any arguments that are left over after option parsing are for
-    // the program. If a file was specified with -f then the filename
-    // is already in the m_option_data.m_args array, and any remaining args
-    // are arguments for the inferior program. If no file was specified with
-    // -f, then what is left is the program name followed by any arguments.
-
-    // Skip any options we consumed with getopt_long_only
-    argc -= optind;
-    argv += optind;
-
-    if (argc > 0) {
-      for (int arg_idx = 0; arg_idx < argc; ++arg_idx) {
-        const char *arg = argv[arg_idx];
-        if (arg)
-          m_option_data.m_args.push_back(arg);
-      }
-    }
-
-  } else {
-    // Skip any options we consumed with getopt_long_only
-    argc -= optind;
-
-    if (argc > 0)
-      ::fprintf(out_fh,
-                "Warning: program arguments are ignored when attaching.\n");
+    return error;
   }
 
   return error;
@@ -884,8 +542,9 @@ static ::FILE *PrepareCommandsForSourcin
   if (err == 0) {
     ssize_t nrwr = write(fds[WRITE], commands_data, commands_size);
     if (nrwr < 0) {
-      fprintf(stderr, "error: write(%i, %p, %" PRIu64 ") failed (errno = %i) "
-                      "when trying to open LLDB commands pipe\n",
+      fprintf(stderr,
+              "error: write(%i, %p, %" PRIu64 ") failed (errno = %i) "
+              "when trying to open LLDB commands pipe\n",
               fds[WRITE], static_cast<const void *>(commands_data),
               static_cast<uint64_t>(commands_size), errno);
     } else if (static_cast<size_t>(nrwr) == commands_size) {
@@ -903,13 +562,13 @@ static ::FILE *PrepareCommandsForSourcin
       // the debugger as an input handle
       commands_file = fdopen(fds[READ], "r");
       if (commands_file) {
-        fds[READ] =
-            -1; // The FILE * 'commands_file' now owns the read descriptor
-                // Hand ownership if the FILE * over to the debugger for
-                // "commands_file".
+        fds[READ] = -1; // The FILE * 'commands_file' now owns the read
+                        // descriptor Hand ownership if the FILE * over to the
+                        // debugger for "commands_file".
       } else {
-        fprintf(stderr, "error: fdopen(%i, \"r\") failed (errno = %i) when "
-                        "trying to open LLDB commands pipe\n",
+        fprintf(stderr,
+                "error: fdopen(%i, \"r\") failed (errno = %i) when "
+                "trying to open LLDB commands pipe\n",
                 fds[READ], errno);
       }
     }
@@ -1206,6 +865,45 @@ void sigcont_handler(int signo) {
   signal(signo, sigcont_handler);
 }
 
+static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) {
+  std::string usage_str = tool_name.str() + "options";
+  table.PrintHelp(llvm::outs(), usage_str.c_str(), "LLDB", false);
+
+  std::string examples = R"___(
+EXAMPLES:
+  The debugger can be started in several modes.
+
+  Passing an executable as a positional argument prepares lldb to debug the
+  given executable. Arguments passed after -- are considered arguments to the
+  debugged executable.
+
+    lldb --arch x86_64 /path/to/program -- --arch arvm7
+
+  Passing one of the attach options causes lldb to immediately attach to the
+  given process.
+
+    lldb -p <pid>
+    lldb -n <process-name>
+
+  Passing --repl starts lldb in REPL mode.
+
+    lldb -r
+
+  Passing --core causes lldb to debug the core file.
+
+    lldb -c /path/to/core
+
+  Command options can be combined with either mode and cause lldb to run the
+  specified commands before or after events, like loading the file or crashing,
+  in the order provided on the command line.
+
+    lldb -O 'settings set stop-disassembly-count 20' -o 'run' -o 'bt'
+    lldb -S /source/before/file -s /source/after/file
+    lldb -K /source/before/crash -k /source/after/crash
+  )___";
+  llvm::outs() << examples;
+}
+
 int
 #ifdef _MSC_VER
 wmain(int argc, wchar_t const *wargv[])
@@ -1224,12 +922,23 @@ main(int argc, char const *argv[])
   const char **argv = argvPointers.data();
 #endif
 
-  llvm::StringRef ToolName = argv[0];
+  // Print stack trace on crash.
+  llvm::StringRef ToolName = llvm::sys::path::filename(argv[0]);
   llvm::sys::PrintStackTraceOnErrorSignal(ToolName);
   llvm::PrettyStackTraceProgram X(argc, argv);
 
-  SBDebugger::Initialize();
+  // Parse arguments.
+  LLDBOptTable T;
+  unsigned MAI, MAC;
+  ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1);
+  opt::InputArgList input_args = T.ParseArgs(arg_arr, MAI, MAC);
+
+  if (input_args.hasArg(OPT_help)) {
+    printHelp(T, ToolName);
+    return 0;
+  }
 
+  SBDebugger::Initialize();
   SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
 
   signal(SIGINT, sigint_handler);
@@ -1247,7 +956,7 @@ main(int argc, char const *argv[])
     Driver driver;
 
     bool exiting = false;
-    SBError error(driver.ParseArgs(argc, argv, stdout, exiting));
+    SBError error(driver.ProcessArgs(input_args, stdout, exiting));
     if (error.Fail()) {
       exit_code = 1;
       const char *error_cstr = error.GetCString();

Modified: lldb/trunk/tools/driver/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Driver.h?rev=347709&r1=347708&r2=347709&view=diff
==============================================================================
--- lldb/trunk/tools/driver/Driver.h (original)
+++ lldb/trunk/tools/driver/Driver.h Tue Nov 27 13:00:32 2018
@@ -12,15 +12,19 @@
 
 #include "Platform.h"
 
-#include <set>
-#include <string>
-#include <vector>
-
 #include "lldb/API/SBBroadcaster.h"
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBError.h"
 
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
 class Driver : public lldb::SBBroadcaster {
 public:
   typedef enum CommandPlacement {
@@ -38,8 +42,8 @@ public:
   /// @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);
+  lldb::SBError ProcessArgs(const llvm::opt::InputArgList &args, FILE *out_fh,
+                            bool &do_exit);
 
   const char *GetFilename() const;
 
@@ -61,13 +65,13 @@ public:
 
     void Clear();
 
-    void AddInitialCommand(const char *command, CommandPlacement placement,
+    void AddInitialCommand(std::string command, CommandPlacement placement,
                            bool is_file, lldb::SBError &error);
 
     struct InitialCmdEntry {
-      InitialCmdEntry(const char *in_contents, bool in_is_file,
+      InitialCmdEntry(std::string contents, bool in_is_file,
                       bool is_cwd_lldbinit_file_read, bool in_quiet = false)
-          : contents(in_contents), is_file(in_is_file),
+          : contents(std::move(contents)), is_file(in_is_file),
             is_cwd_lldbinit_file_read(is_cwd_lldbinit_file_read),
             source_quietly(in_quiet) {}
 
@@ -89,7 +93,6 @@ public:
     bool m_source_quietly;
     bool m_print_version;
     bool m_print_python_path;
-    bool m_print_help;
     bool m_wait_for;
     bool m_repl;
     lldb::LanguageType m_repl_lang;

Added: lldb/trunk/tools/driver/Options.td
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Options.td?rev=347709&view=auto
==============================================================================
--- lldb/trunk/tools/driver/Options.td (added)
+++ lldb/trunk/tools/driver/Options.td Tue Nov 27 13:00:32 2018
@@ -0,0 +1,218 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name>: Flag<["--", "-"], name>;
+class S<string name>: Separate<["--", "-"], name>;
+class R<list<string> prefixes, string name>
+  : Option<prefixes, name, KIND_REMAINING_ARGS>;
+
+// Attaching options.
+def grp_attach : OptionGroup<"attaching">, HelpText<"ATTACHING">;
+
+def attach_name: Separate<["--", "-"], "attach-name">,
+  MetaVarName<"<name>">,
+  HelpText<"Tells the debugger to attach to a process with the given name.">,
+  Group<grp_attach>;
+def: Separate<["-"], "n">,
+  Alias<attach_name>,
+  HelpText<"Alias for --attach-name">,
+  Group<grp_attach>;
+
+def wait_for: F<"wait-for">,
+  HelpText<"Tells the debugger to wait for a process with the given pid or name to launch before attaching.">,
+  Group<grp_attach>;
+def: Flag<["-"], "w">,
+  Alias<wait_for>,
+  HelpText<"Alias for --wait-for">,
+  Group<grp_attach>;
+
+def attach_pid: Separate<["--", "-"], "attach-pid">,
+  MetaVarName<"<pid>">,
+  HelpText<"Tells the debugger to attach to a process with the given pid.">,
+  Group<grp_attach>;
+def: Separate<["-"], "p">,
+  Alias<attach_pid>,
+  HelpText<"Alias for --attach-pid">,
+  Group<grp_attach>;
+
+
+// Scripting options.
+def grp_scripting : OptionGroup<"scripting">, HelpText<"SCRIPTING">;
+
+def python_path: F<"python-path">,
+  HelpText<"Prints out the path to the lldb.py file for this version of lldb.">,
+  Group<grp_scripting>;
+def: Flag<["-"], "P">,
+  Alias<python_path>,
+  HelpText<"Alias for --python-path">,
+  Group<grp_scripting>;
+
+def script_language: Separate<["--", "-"], "script-language">, MetaVarName<"<language>">,
+  HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">,
+  Group<grp_scripting>;
+def: Separate<["-"], "l">,
+  Alias<script_language>,
+  HelpText<"Alias for --script-language">,
+  Group<grp_scripting>;
+
+// Repl options.
+def grp_repl : OptionGroup<"repl">, HelpText<"REPL">;
+
+def repl: Separate<["--", "-"], "repl">,
+  HelpText<"Runs lldb in REPL mode with a stub process.">,
+  Group<grp_repl>;
+def: Separate<["-"], "r">,
+  Alias<repl>,
+  HelpText<"Alias for --repl">,
+  Group<grp_repl>;
+
+def repl_language: Separate<["--", "-"], "repl-language">,
+  MetaVarName<"<language>">,
+  HelpText<"Chooses the language for the REPL.">,
+  Group<grp_repl>;
+def: Separate<["-"], "R">,
+  Alias<repl_language>,
+  HelpText<"Alias for --repl-language">,
+  Group<grp_repl>;
+
+
+// Command options.
+def grp_command : OptionGroup<"command">, HelpText<"COMMANDS">;
+
+def no_lldbinit: F<"no-lldbinit">,
+  HelpText<"Do not automatically parse any '.lldbinit' files.">,
+  Group<grp_command>;
+def: Flag<["-"], "x">,
+  Alias<no_lldbinit>,
+  HelpText<"Alias for --no-lldbinit">,
+  Group<grp_command>;
+
+def batch: F<"batch">,
+  HelpText<"Tells the debugger to run the commands from -s, -S, -o & -O, and then quit.">,
+  Group<grp_command>;
+def: Flag<["-"], "b">,
+  Alias<batch>,
+  HelpText<"Alias for --batch">,
+  Group<grp_command>;
+
+def source_quietly: F<"source-quietly">,
+  HelpText<"Tells the debugger to execute this one-line lldb command before any file has been loaded.">,
+  Group<grp_command>;
+def: Flag<["-"], "Q">,
+  Alias<source_quietly>,
+  HelpText<"Alias for --source-quietly">,
+  Group<grp_command>;
+
+def one_line_on_crash: Separate<["--", "-"], "one-line-on-crash">,
+  MetaVarName<"<command>">,
+  HelpText<"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes.">,
+  Group<grp_command>;
+def: Separate<["-"], "k">,
+  Alias<one_line_on_crash>,
+  HelpText<"Alias for --one-line-on-crash">,
+  Group<grp_command>;
+
+def source_on_crash: Separate<["--", "-"], "source-on-crash">,
+  MetaVarName<"<file>">,
+  HelpText<"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes.">,
+  Group<grp_command>;
+def: Separate<["-"], "K">,
+  Alias<source_on_crash>,
+  HelpText<"Alias for --source-on-crash">,
+  Group<grp_command>;
+
+def source: Separate<["--", "-"], "source">,
+  MetaVarName<"<file>">,
+  HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, after any file has been loaded.">,
+  Group<grp_command>;
+def: Separate<["-"], "s">,
+  Alias<source>,
+  HelpText<"Alias for --source">,
+  Group<grp_command>;
+
+def source_before_file: Separate<["--", "-"], "source-before-file">,
+  MetaVarName<"<file>">,
+  HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, before any file has been loaded.">,
+  Group<grp_command>;
+def: Separate<["-"], "S">,
+  Alias<source_before_file>,
+  HelpText<"Alias for --source-before-file">,
+  Group<grp_command>;
+
+def one_line: Separate<["--", "-"], "one-line">,
+  MetaVarName<"<command>">,
+  HelpText<"Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded.">,
+  Group<grp_command>;
+def: Separate<["-"], "o">,
+  Alias<one_line>,
+  HelpText<"Alias for --one-line">,
+  Group<grp_command>;
+
+def one_line_before_file: Separate<["--", "-"], "one-line-before-file">,
+  MetaVarName<"<command>">,
+  HelpText<"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded.">,
+  Group<grp_command>;
+def: Separate<["-"], "O">,
+  Alias<one_line_before_file>,
+  HelpText<"Alias for --one-line-before-file">,
+  Group<grp_command>;
+
+
+// General options.
+def version: F<"version">,
+  HelpText<"Prints out the current version number of the LLDB debugger.">;
+def: Flag<["-"], "v">,
+  Alias<version>,
+  HelpText<"Alias for --version">;
+
+def help: F<"help">,
+  HelpText<"Prints out the usage information for the LLDB debugger.">;
+def: Flag<["-"], "h">,
+  Alias<help>,
+  HelpText<"Alias for --help">;
+
+def core: F<"core">,
+  HelpText<"Tells the debugger to use the full path to <core> as the core file.">;
+def: Flag<["-"], "c">,
+  Alias<core>,
+  HelpText<"Alias for --core">;
+
+def editor: F<"editor">,
+  HelpText<"Tells the debugger to open source files using the host's \"external editor\" mechanism.">;
+def: Flag<["-"], "e">,
+  Alias<editor>,
+  HelpText<"Alias for --editor">;
+
+def no_use_colors: F<"no-use-colors">,
+  HelpText<"Do not use colors.">;
+def: Flag<["-"], "X">,
+  Alias<no_use_colors>,
+  HelpText<"Alias for --no-use-color">;
+
+def file: Separate<["--", "-"], "file">,
+  MetaVarName<"<filename>">,
+  HelpText<"Tells the debugger to use the file <filename> as the program to be debugged.">;
+def: Separate<["-"], "f">,
+  Alias<file>,
+  HelpText<"Alias for --file">;
+
+def arch: Separate<["--", "-"], "arch">,
+  MetaVarName<"<architecture>">,
+  HelpText<"Tells the debugger to use the specified architecture when starting and running the program.">;
+def: Separate<["-"], "a">,
+  Alias<arch>,
+  HelpText<"Alias for --arch">;
+
+def debug: F<"debug">,
+  HelpText<"Tells the debugger to print out extra information for debugging itself.">;
+def: Flag<["-"], "d">,
+  Alias<debug>,
+  HelpText<"Alias for --debug">;
+
+def reproducer: Separate<["--", "-"], "reproducer">,
+  MetaVarName<"<filename>">,
+  HelpText<"Tells the debugger to use the fullpath to <filename> as a reproducer.">;
+def: Separate<["-"], "z">,
+  Alias<file>,
+  HelpText<"Alias for --reproducer">;
+
+def REM : R<["--"], "">;




More information about the lldb-commits mailing list