Index: lldb/trunk/include/lldb/Interpreter/OptionValue.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/OptionValue.h +++ lldb/trunk/include/lldb/Interpreter/OptionValue.h @@ -58,9 +58,11 @@ eDumpOptionValue = (1u << 2), eDumpOptionDescription = (1u << 3), eDumpOptionRaw = (1u << 4), + eDumpOptionCommand = (1u << 5), eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue), eDumpGroupHelp = - (eDumpOptionName | eDumpOptionType | eDumpOptionDescription) + (eDumpOptionName | eDumpOptionType | eDumpOptionDescription), + eDumpGroupExport = (eDumpOptionCommand | eDumpOptionName | eDumpOptionValue) }; OptionValue() Index: lldb/trunk/lit/Settings/TestExport.test =================================================================== --- lldb/trunk/lit/Settings/TestExport.test +++ lldb/trunk/lit/Settings/TestExport.test @@ -0,0 +1,32 @@ +# This tests writing and reading settings from LLDB. + +# Check that the settings can be written to file and read again without +# altering the values. +# RUN: %lldb -b -o 'settings write -f %t.foo' -o 'settings read -f %t.foo' -o 'settings export %t.bar' -o 'settings read -f %t.bar' 2>&1 | FileCheck %s --check-prefix SUCCESS +# RUN: diff -w %t.foo %t.bar +# SUCCESS-NOT: error: + +# Check that exporting target settings only export target settings and nothing else. +# RUN: %lldb -b -o 'settings write -f %t.target target' 2>&1 | FileCheck %s --check-prefix SUCCESS +# RUN: cat %t.target | FileCheck %s --check-prefix TARGET +# TARGET: settings set target +# TARGET-NOT: settings set platform +# TARGET-NOT: settings set symbols +# TARGET-NOT: settings set interpreter +# TARGET-NOT: settings set plugin + +# Check that settings appear twice when appending. +# RUN: %lldb -b -o 'settings write -a -f %t.append target' -o 'settings write -a -f %t.append target' 2>&1 | FileCheck %s --check-prefix SUCCESS +# RUN: cat %t.append | FileCheck %s --check-prefix APPEND +# APPEND: settings set target.language +# APPEND: settings set target.language + +# Check that an error is printed for non-existing setting. +# RUN: echo "settings set bogus" > %t.bogus_setting +# RUN: %lldb -b -o 'settings read -f %t.bogus_setting' 2>&1 | FileCheck %s --check-prefix BOGUS-SETTING +# BOGUS-SETTING: error: invalid value path + +# Check that an error is printed for invalid value. +# RUN: echo "settings set target.language bogus" > %t.bogus_value +# RUN: %lldb -b -o 'settings read -f %t.bogus_value' 2>&1 | FileCheck %s --check-prefix BOGUS-VALUE +# BOGUS-VALUE: error: invalid language type Index: lldb/trunk/source/Commands/CommandObjectSettings.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectSettings.cpp +++ lldb/trunk/source/Commands/CommandObjectSettings.cpp @@ -321,6 +321,207 @@ }; //------------------------------------------------------------------------- +// CommandObjectSettingsWrite -- Write settings to file +//------------------------------------------------------------------------- + +static constexpr OptionDefinition g_settings_write_options[] = { + // clang-format off + { LLDB_OPT_SET_ALL, true, "file", 'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file into which to write the settings." }, + { LLDB_OPT_SET_ALL, false, "append",'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Append to saved settings file if it exists."}, + // clang-format on +}; + +class CommandObjectSettingsWrite : public CommandObjectParsed { +public: + CommandObjectSettingsWrite(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "settings export", + "Write matching debugger settings and their " + "current values to a file that can be read in with " + "\"settings read\". Defaults to writing all settings.", + nullptr), + m_options() { + CommandArgumentEntry arg1; + CommandArgumentData var_name_arg; + + // Define the first (and only) variant of this arg. + var_name_arg.arg_type = eArgTypeSettingVariableName; + var_name_arg.arg_repetition = eArgRepeatOptional; + + // There is only one variant this argument could be; put it into the + // argument entry. + arg1.push_back(var_name_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back(arg1); + } + + ~CommandObjectSettingsWrite() override = default; + + Options *GetOptions() override { return &m_options; } + + class CommandOptions : public Options { + public: + CommandOptions() : Options() {} + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'f': + m_filename.assign(option_arg); + break; + case 'a': + m_append = true; + break; + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", + short_option); + break; + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_filename.clear(); + m_append = false; + } + + llvm::ArrayRef GetDefinitions() override { + return llvm::makeArrayRef(g_settings_write_options); + } + + // Instance variables to hold the values for command options. + std::string m_filename; + bool m_append = false; + }; + +protected: + bool DoExecute(Args &args, CommandReturnObject &result) override { + std::string path(FileSpec(m_options.m_filename, true).GetPath()); + uint32_t options = File::OpenOptions::eOpenOptionWrite | + File::OpenOptions::eOpenOptionCanCreate; + if (m_options.m_append) + options |= File::OpenOptions::eOpenOptionAppend; + else + options |= File::OpenOptions::eOpenOptionTruncate; + + StreamFile out_file(path.c_str(), options, + lldb::eFilePermissionsFileDefault); + + if (!out_file.GetFile().IsValid()) { + result.AppendErrorWithFormat("%s: unable to write to file", path.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // Exporting should not be context sensitive. + ExecutionContext clean_ctx; + + if (args.empty()) { + m_interpreter.GetDebugger().DumpAllPropertyValues( + &clean_ctx, out_file, OptionValue::eDumpGroupExport); + return result.Succeeded(); + } + + for (const auto &arg : args) { + Status error(m_interpreter.GetDebugger().DumpPropertyValue( + &clean_ctx, out_file, arg.ref, OptionValue::eDumpGroupExport)); + if (!error.Success()) { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingsRead -- Read settings from file +//------------------------------------------------------------------------- + +static constexpr OptionDefinition g_settings_read_options[] = { + // clang-format off + {LLDB_OPT_SET_ALL, true, "file",'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename, "The file from which to read the breakpoints." }, + // clang-format on +}; + +class CommandObjectSettingsRead : public CommandObjectParsed { +public: + CommandObjectSettingsRead(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "settings read", + "Read settings previously saved to a file with \"settings write\".", + nullptr), + m_options() {} + + ~CommandObjectSettingsRead() override = default; + + Options *GetOptions() override { return &m_options; } + + class CommandOptions : public Options { + public: + CommandOptions() : Options() {} + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'f': + m_filename.assign(option_arg); + break; + default: + error.SetErrorStringWithFormat("unrecognized option '%c'", + short_option); + break; + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_filename.clear(); + } + + llvm::ArrayRef GetDefinitions() override { + return llvm::makeArrayRef(g_settings_read_options); + } + + // Instance variables to hold the values for command options. + std::string m_filename; + }; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + FileSpec file(m_options.m_filename, true); + ExecutionContext clean_ctx; + CommandInterpreterRunOptions options; + options.SetAddToHistory(false); + options.SetEchoCommands(false); + options.SetPrintResults(true); + options.SetStopOnError(false); + m_interpreter.HandleCommandsFromFile(file, &clean_ctx, options, result); + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +//------------------------------------------------------------------------- // CommandObjectSettingsList -- List settable variables //------------------------------------------------------------------------- @@ -1007,6 +1208,10 @@ CommandObjectSP(new CommandObjectSettingsAppend(interpreter))); LoadSubCommand("clear", CommandObjectSP(new CommandObjectSettingsClear(interpreter))); + LoadSubCommand("write", + CommandObjectSP(new CommandObjectSettingsWrite(interpreter))); + LoadSubCommand("read", + CommandObjectSP(new CommandObjectSettingsRead(interpreter))); } CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings() = default; Index: lldb/trunk/source/Interpreter/OptionValueArray.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueArray.cpp +++ lldb/trunk/source/Interpreter/OptionValueArray.cpp @@ -31,13 +31,17 @@ strm.Printf("(%s)", GetTypeAsCString()); } if (dump_mask & eDumpOptionValue) { - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", (m_values.size() > 0) ? "\n" : ""); - strm.IndentMore(); + const bool one_line = dump_mask & eDumpOptionCommand; const uint32_t size = m_values.size(); + if (dump_mask & eDumpOptionType) + strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : ""); + if (!one_line) + strm.IndentMore(); for (uint32_t i = 0; i < size; ++i) { - strm.Indent(); - strm.Printf("[%u]: ", i); + if (!one_line) { + strm.Indent(); + strm.Printf("[%u]: ", i); + } const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; switch (array_element_type) { default: @@ -63,10 +67,16 @@ extra_dump_options); break; } - if (i < (size - 1)) - strm.EOL(); + + if (!one_line) { + if (i < (size - 1)) + strm.EOL(); + } else { + strm << ' '; + } } - strm.IndentLess(); + if (!one_line) + strm.IndentLess(); } } Index: lldb/trunk/source/Interpreter/OptionValueDictionary.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueDictionary.cpp +++ lldb/trunk/source/Interpreter/OptionValueDictionary.cpp @@ -33,16 +33,23 @@ strm.Printf("(%s)", GetTypeAsCString()); } if (dump_mask & eDumpOptionValue) { + const bool one_line = dump_mask & eDumpOptionCommand; if (dump_mask & eDumpOptionType) strm.PutCString(" ="); collection::iterator pos, end = m_values.end(); - strm.IndentMore(); + if (!one_line) + strm.IndentMore(); for (pos = m_values.begin(); pos != end; ++pos) { OptionValue *option_value = pos->second.get(); - strm.EOL(); + + if (one_line) + strm << ' '; + else + strm.EOL(); + strm.Indent(pos->first.GetCString()); const uint32_t extra_dump_options = m_raw_value_dump ? eDumpOptionRaw : 0; @@ -74,7 +81,8 @@ break; } } - strm.IndentLess(); + if (!one_line) + strm.IndentLess(); } } Index: lldb/trunk/source/Interpreter/OptionValueFileSpecLIst.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueFileSpecLIst.cpp +++ lldb/trunk/source/Interpreter/OptionValueFileSpecLIst.cpp @@ -25,16 +25,24 @@ if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", m_current_value.GetSize() > 0 ? "\n" : ""); - strm.IndentMore(); + const bool one_line = dump_mask & eDumpOptionCommand; const uint32_t size = m_current_value.GetSize(); + if (dump_mask & eDumpOptionType) + strm.Printf(" =%s", + (m_current_value.GetSize() > 0 && !one_line) ? "\n" : ""); + if (!one_line) + strm.IndentMore(); for (uint32_t i = 0; i < size; ++i) { - strm.Indent(); - strm.Printf("[%u]: ", i); + if (!one_line) { + strm.Indent(); + strm.Printf("[%u]: ", i); + } m_current_value.GetFileSpecAtIndex(i).Dump(&strm); + if (one_line) + strm << ' '; } - strm.IndentLess(); + if (!one_line) + strm.IndentLess(); } } Index: lldb/trunk/source/Interpreter/OptionValueFormatEntity.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueFormatEntity.cpp +++ lldb/trunk/source/Interpreter/OptionValueFormatEntity.cpp @@ -61,10 +61,10 @@ strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) - strm.PutCString(" = \""); + strm.PutCString(" = "); std::string escaped; EscapeBackticks(m_current_format, escaped); - strm << escaped << '"'; + strm << '"' << escaped << '"'; } } Index: lldb/trunk/source/Interpreter/OptionValueLanguage.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionValueLanguage.cpp +++ lldb/trunk/source/Interpreter/OptionValueLanguage.cpp @@ -28,7 +28,8 @@ if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - strm.PutCString(Language::GetNameForLanguageType(m_current_value)); + if (m_current_value != eLanguageTypeUnknown) + strm.PutCString(Language::GetNameForLanguageType(m_current_value)); } } Index: lldb/trunk/source/Interpreter/Property.cpp =================================================================== --- lldb/trunk/source/Interpreter/Property.cpp +++ lldb/trunk/source/Interpreter/Property.cpp @@ -233,7 +233,10 @@ uint32_t dump_mask) const { if (m_value_sp) { const bool dump_desc = dump_mask & OptionValue::eDumpOptionDescription; + const bool dump_cmd = dump_mask & OptionValue::eDumpOptionCommand; const bool transparent = m_value_sp->ValueIsTransparent(); + if (dump_cmd && !transparent) + strm << "settings set -f "; if (dump_desc || !transparent) { if ((dump_mask & OptionValue::eDumpOptionName) && m_name) { DumpQualifiedName(strm);