JDevlieghere updated this revision to Diff 175377. JDevlieghere added a comment.
Will land this tomorrow once we've figured out how to integrate tablegen with the Xcode project. In the meantime I found some issues due to ordering of command options. Their relative order matters and we had tests relying on this. For example consider what happens when you say lldb -o 'do one thing' -s 'source/some/file' You'd expect that the one-line is executed before sourcing the file. This wasn't the case with the previous version of this patch and is now fixed. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D54692/new/ https://reviews.llvm.org/D54692 Files: tools/driver/CMakeLists.txt tools/driver/Driver.cpp tools/driver/Driver.h tools/driver/Options.td
Index: tools/driver/Options.td =================================================================== --- /dev/null +++ tools/driver/Options.td @@ -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<["--"], "">; Index: tools/driver/Driver.h =================================================================== --- tools/driver/Driver.h +++ tools/driver/Driver.h @@ -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 @@ /// @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 @@ 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 @@ 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; Index: tools/driver/Driver.cpp =================================================================== --- tools/driver/Driver.cpp +++ tools/driver/Driver.cpp @@ -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 @@ } } -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() { 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 @@ // 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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. +// 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(); + + // 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); -SBError Driver::ParseArgs(int argc, const char *argv[], FILE *out_fh, - bool &exiting) { - static_assert(g_num_options > 0, "cannot handle arguments"); + if (args.hasArg(OPT_version)) { + m_option_data.m_print_version = true; + } - ResetOptionValues(); + if (args.hasArg(OPT_python_path)) { + m_option_data.m_print_python_path = true; + } - SBError error; - std::vector<option> long_options_vector; - BuildGetOptTable(long_options_vector); - if (long_options_vector.empty()) { - error.SetErrorStringWithFormat("invalid long options"); - return error; + if (args.hasArg(OPT_batch)) { + m_option_data.m_batch = 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_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; - - 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; - - 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_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; + } + } + + 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; + } + } + + 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 @@ } 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 @@ 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 @@ // 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,33 @@ signal(signo, sigcont_handler); } +static void printHelp(LLDBOptTable &table, llvm::StringRef tool_name) { + std::string usage_str; + llvm::StringRef indent = " "; + llvm::raw_string_ostream usage(usage_str); + usage << '\n'; + usage << indent << tool_name << " -h" << '\n'; + usage << indent << tool_name + << " -v [[--] <PROGRAM-ARG-1> [<PROGRAM_ARG-2> ...]]\n"; + usage << indent << tool_name + << " -a <arch> -f <filename> [-c <filename>] [-s <filename>] [-o " + "<none>] [-S <filename>] [-O <none>] [-k <none>] [-K <filename>] " + "[-Q] [-b] [-e] [-x] [-X] [-l <script-language>] [-d] [-z " + "<filename>] [[--] <PROGRAM-ARG-1> [<PROGRAM_ARG-2> ...]]\n"; + usage << indent << tool_name + << " -n <process-name> -w [-s <filename>] [-o <none>] [-S " + "<filename>] [-O <none>] [-k <none>] [-K <filename>] [-Q] [-b] " + "[-e] [-x] [-X] [-l <script-language>] [-d] [-z <filename>]\n"; + usage << indent << tool_name + << " -p <pid> [-s <filename>] [-o <none>] [-S <filename>] [-O " + "<none>] [-k <none>] [-K <filename>] [-Q] [-b] [-e] [-x] [-X] [-l " + "<script-language>] [-d] [-z <filename>]\n"; + usage << indent << tool_name << " -P\n"; + usage << indent << tool_name << " -r [<none>] -R <none>\n"; + usage.flush(); + table.PrintHelp(llvm::outs(), usage_str.c_str(), "LLDB", false); +} + int #ifdef _MSC_VER wmain(int argc, wchar_t const *wargv[]) @@ -1224,12 +910,23 @@ 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 +944,7 @@ 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(); Index: tools/driver/CMakeLists.txt =================================================================== --- tools/driver/CMakeLists.txt +++ tools/driver/CMakeLists.txt @@ -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 @@ ${host_lib} LINK_COMPONENTS + Option Support ) @@ -24,4 +29,8 @@ add_definitions( -DIMPORT_LIBLLDB ) endif() -add_dependencies(lldb ${LLDB_SUITE_TARGET}) +add_dependencies(lldb + ${LLDB_SUITE_TARGET} + LLDBOptionsTableGen + ${tablegen_deps} +)
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits