diff --git a/CHANGES.txt b/CHANGES.txt index 2a86a63c5a..94fe94e1fd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,11 @@ be normally except without a tag before each value (thus, they are tightly "packed"). + protoc + * --error_format=msvs option causes errors to be printed in Visual Studio + format, which should allow them to be clicked on in the build log to go + directly to the error location. + C++ * UnknownFieldSet now supports STL-like iteration. * Message interface has method ParseFromBoundedZeroCopyStream() which parses diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index e212467e3d..3755f743f2 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -55,3 +55,5 @@ Non-Google patch contributors: * Solaris 10 + Sun Studio fix. Alek Storm * Slicing support for repeated scalar fields for the Python API. + Oleg Smolsky + * MS Visual Studio error format option. diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index 09473ec799..3629f682c0 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -132,18 +132,29 @@ void SetFdToBinaryMode(int fd) { class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, public io::ErrorCollector { public: - ErrorPrinter() {} + ErrorPrinter(ErrorFormat format) : format_(format) {} ~ErrorPrinter() {} // implements MultiFileErrorCollector ------------------------------ void AddError(const string& filename, int line, int column, const string& message) { + + cerr << filename; + // Users typically expect 1-based line/column numbers, so we add 1 // to each here. - cerr << filename; if (line != -1) { - cerr << ":" << (line + 1) << ":" << (column + 1); + // Allow for both GCC- and Visual-Studio-compatible output. + switch (format_) { + case CommandLineInterface::ERROR_FORMAT_GCC: + cerr << ":" << (line + 1) << ":" << (column + 1); + break; + case CommandLineInterface::ERROR_FORMAT_MSVS: + cerr << "(" << (line + 1) << ") : error in column=" << (column + 1); + break; + } } + cerr << ": " << message << endl; } @@ -151,6 +162,9 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, void AddError(int line, int column, const string& message) { AddError("input", line, column, message); } + + private: + const ErrorFormat format_; }; // ------------------------------------------------------------------- @@ -294,6 +308,7 @@ CommandLineInterface::ErrorReportingFileOutput::~ErrorReportingFileOutput() { CommandLineInterface::CommandLineInterface() : mode_(MODE_COMPILE), + error_format_(ERROR_FORMAT_GCC), imports_in_descriptor_set_(false), disallow_services_(false), inputs_are_proto_path_relative_(false) {} @@ -326,7 +341,7 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { } // Allocate the Importer. - ErrorPrinter error_collector; + ErrorPrinter error_collector(error_format_); Importer importer(&source_tree, &error_collector); vector parsed_files; @@ -657,6 +672,16 @@ bool CommandLineInterface::InterpretArgument(const string& name, codec_type_ = value; + } else if (name == "--error_format") { + if (value == "gcc") { + error_format_ = ERROR_FORMAT_GCC; + } else if (value == "msvs") { + error_format_ = ERROR_FORMAT_MSVS; + } else { + cerr << "Unknown error format: " << value << endl; + return false; + } + } else { // Some other flag. Look it up in the generators list. GeneratorMap::const_iterator iter = generators_.find(name); @@ -722,7 +747,10 @@ void CommandLineInterface::PrintHelpText() { " the input files to FILE.\n" " --include_imports When using --descriptor_set_out, also include\n" " all dependencies of the input files in the\n" -" set, so that the set is self-contained." << endl; +" set, so that the set is self-contained.\n" +" --error_format=FORMAT Set the format in which to print errors.\n" +" FORMAT may be 'gcc' (the default) or 'msvs'\n" +" (Microsoft Visual Studio format)." << endl; for (GeneratorMap::iterator iter = generators_.begin(); iter != generators_.end(); ++iter) { @@ -788,7 +816,7 @@ bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) { if (mode_ == MODE_ENCODE) { // Input is text. - ErrorPrinter error_collector; + ErrorPrinter error_collector(error_format_); TextFormat::Parser parser; parser.RecordErrorsTo(&error_collector); parser.AllowPartialMessage(true); diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h index 86eeba5379..ec658636ab 100644 --- a/src/google/protobuf/compiler/command_line_interface.h +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -210,6 +210,13 @@ class LIBPROTOC_EXPORT CommandLineInterface { Mode mode_; + enum ErrorFormat { + ERROR_FORMAT_GCC, // GCC error output format (default). + ERROR_FORMAT_MSVS // Visual Studio output (--error_format=msvs). + }; + + ErrorFormat error_format_; + vector > proto_path_; // Search path for proto files. vector input_files_; // Names of the input proto files. diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index 0780729d3c..af765b8725 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -1047,6 +1047,59 @@ TEST_F(CommandLineInterfaceTest, HelpText) { ExpectErrorSubstring("Test error output."); } +TEST_F(CommandLineInterfaceTest, GccFormatErrors) { + // Test --error_format=gcc (which is the default, but we want to verify + // that it can be set explicitly). + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "badsyntax\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir --error_format=gcc foo.proto"); + + ExpectErrorText( + "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n"); +} + +TEST_F(CommandLineInterfaceTest, MsvsFormatErrors) { + // Test --error_format=msvs + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "badsyntax\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir --error_format=msvs foo.proto"); + + ExpectErrorText( + "foo.proto(2) : error in column=1: Expected top-level statement " + "(e.g. \"message\").\n"); +} + +TEST_F(CommandLineInterfaceTest, InvalidErrorFormat) { + // Test --error_format=msvs + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "badsyntax\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir --error_format=invalid foo.proto"); + + ExpectErrorText( + "Unknown error format: invalid\n"); +} + // ------------------------------------------------------------------- // Flag parsing tests