diff options
Diffstat (limited to 'src/google')
5 files changed, 363 insertions, 81 deletions
diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rb b/src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rb index 49b23fbe..0c9ed6fb 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rb +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rb @@ -4,62 +4,64 @@ require 'google/protobuf' Google::Protobuf::DescriptorPool.generated_pool.build do - add_message "A.B.C.TestMessage" do - optional :optional_int32, :int32, 1 - optional :optional_int64, :int64, 2 - optional :optional_uint32, :uint32, 3 - optional :optional_uint64, :uint64, 4 - optional :optional_bool, :bool, 5 - optional :optional_double, :double, 6 - optional :optional_float, :float, 7 - optional :optional_string, :string, 8 - optional :optional_bytes, :bytes, 9 - optional :optional_enum, :enum, 10, "A.B.C.TestEnum" - optional :optional_msg, :message, 11, "A.B.C.TestMessage" - repeated :repeated_int32, :int32, 21 - repeated :repeated_int64, :int64, 22 - repeated :repeated_uint32, :uint32, 23 - repeated :repeated_uint64, :uint64, 24 - repeated :repeated_bool, :bool, 25 - repeated :repeated_double, :double, 26 - repeated :repeated_float, :float, 27 - repeated :repeated_string, :string, 28 - repeated :repeated_bytes, :bytes, 29 - repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum" - repeated :repeated_msg, :message, 31, "A.B.C.TestMessage" - map :map_int32_string, :int32, :string, 61 - map :map_int64_string, :int64, :string, 62 - map :map_uint32_string, :uint32, :string, 63 - map :map_uint64_string, :uint64, :string, 64 - map :map_bool_string, :bool, :string, 65 - map :map_string_string, :string, :string, 66 - map :map_string_msg, :string, :message, 67, "A.B.C.TestMessage" - map :map_string_enum, :string, :enum, 68, "A.B.C.TestEnum" - map :map_string_int32, :string, :int32, 69 - map :map_string_bool, :string, :bool, 70 - optional :nested_message, :message, 80, "A.B.C.TestMessage.NestedMessage" - oneof :my_oneof do - optional :oneof_int32, :int32, 41 - optional :oneof_int64, :int64, 42 - optional :oneof_uint32, :uint32, 43 - optional :oneof_uint64, :uint64, 44 - optional :oneof_bool, :bool, 45 - optional :oneof_double, :double, 46 - optional :oneof_float, :float, 47 - optional :oneof_string, :string, 48 - optional :oneof_bytes, :bytes, 49 - optional :oneof_enum, :enum, 50, "A.B.C.TestEnum" - optional :oneof_msg, :message, 51, "A.B.C.TestMessage" + add_file("ruby_generated_code.proto", :syntax => :proto3) do + add_message "A.B.C.TestMessage" do + optional :optional_int32, :int32, 1 + optional :optional_int64, :int64, 2 + optional :optional_uint32, :uint32, 3 + optional :optional_uint64, :uint64, 4 + optional :optional_bool, :bool, 5 + optional :optional_double, :double, 6 + optional :optional_float, :float, 7 + optional :optional_string, :string, 8 + optional :optional_bytes, :bytes, 9 + optional :optional_enum, :enum, 10, "A.B.C.TestEnum" + optional :optional_msg, :message, 11, "A.B.C.TestMessage" + repeated :repeated_int32, :int32, 21 + repeated :repeated_int64, :int64, 22 + repeated :repeated_uint32, :uint32, 23 + repeated :repeated_uint64, :uint64, 24 + repeated :repeated_bool, :bool, 25 + repeated :repeated_double, :double, 26 + repeated :repeated_float, :float, 27 + repeated :repeated_string, :string, 28 + repeated :repeated_bytes, :bytes, 29 + repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum" + repeated :repeated_msg, :message, 31, "A.B.C.TestMessage" + map :map_int32_string, :int32, :string, 61 + map :map_int64_string, :int64, :string, 62 + map :map_uint32_string, :uint32, :string, 63 + map :map_uint64_string, :uint64, :string, 64 + map :map_bool_string, :bool, :string, 65 + map :map_string_string, :string, :string, 66 + map :map_string_msg, :string, :message, 67, "A.B.C.TestMessage" + map :map_string_enum, :string, :enum, 68, "A.B.C.TestEnum" + map :map_string_int32, :string, :int32, 69 + map :map_string_bool, :string, :bool, 70 + optional :nested_message, :message, 80, "A.B.C.TestMessage.NestedMessage" + oneof :my_oneof do + optional :oneof_int32, :int32, 41 + optional :oneof_int64, :int64, 42 + optional :oneof_uint32, :uint32, 43 + optional :oneof_uint64, :uint64, 44 + optional :oneof_bool, :bool, 45 + optional :oneof_double, :double, 46 + optional :oneof_float, :float, 47 + optional :oneof_string, :string, 48 + optional :oneof_bytes, :bytes, 49 + optional :oneof_enum, :enum, 50, "A.B.C.TestEnum" + optional :oneof_msg, :message, 51, "A.B.C.TestMessage" + end + end + add_message "A.B.C.TestMessage.NestedMessage" do + optional :foo, :int32, 1 + end + add_enum "A.B.C.TestEnum" do + value :Default, 0 + value :A, 1 + value :B, 2 + value :C, 3 end - end - add_message "A.B.C.TestMessage.NestedMessage" do - optional :foo, :int32, 1 - end - add_enum "A.B.C.TestEnum" do - value :Default, 0 - value :A, 1 - value :B, 2 - value :C, 3 end end diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2.proto b/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2.proto new file mode 100644 index 00000000..8d3cc13e --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2.proto @@ -0,0 +1,68 @@ +syntax = "proto2"; + +package A.B.C; + +message TestMessage { + optional int32 optional_int32 = 1 [default = 1]; + optional int64 optional_int64 = 2 [default = 2]; + optional uint32 optional_uint32 = 3 [default = 3]; + optional uint64 optional_uint64 = 4 [default = 4]; + optional bool optional_bool = 5 [default = true]; + optional double optional_double = 6 [default = 6.0]; + optional float optional_float = 7 [default = 7.0]; + optional string optional_string = 8 [default = "default str"]; + optional bytes optional_bytes = 9 [default = "\0\1\2\100fubar"]; + optional TestEnum optional_enum = 10 [default = A]; + optional TestMessage optional_msg = 11; + + repeated int32 repeated_int32 = 21; + repeated int64 repeated_int64 = 22; + repeated uint32 repeated_uint32 = 23; + repeated uint64 repeated_uint64 = 24; + repeated bool repeated_bool = 25; + repeated double repeated_double = 26; + repeated float repeated_float = 27; + repeated string repeated_string = 28; + repeated bytes repeated_bytes = 29; + repeated TestEnum repeated_enum = 30; + repeated TestMessage repeated_msg = 31; + + required int32 required_int32 = 41; + required int64 required_int64 = 42; + required uint32 required_uint32 = 43; + required uint64 required_uint64 = 44; + required bool required_bool = 45; + required double required_double = 46; + required float required_float = 47; + required string required_string = 48; + required bytes required_bytes = 49; + required TestEnum required_enum = 50; + required TestMessage required_msg = 51; + + oneof my_oneof { + int32 oneof_int32 = 61; + int64 oneof_int64 = 62; + uint32 oneof_uint32 = 63; + uint64 oneof_uint64 = 64; + bool oneof_bool = 65; + double oneof_double = 66; + float oneof_float = 67; + string oneof_string = 68; + bytes oneof_bytes = 69; + TestEnum oneof_enum = 70; + TestMessage oneof_msg = 71; + } + + message NestedMessage { + optional int32 foo = 1; + } + + optional NestedMessage nested_message = 80; +} + +enum TestEnum { + Default = 0; + A = 1; + B = 2; + C = 3; +} diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2_pb.rb b/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2_pb.rb new file mode 100644 index 00000000..15b340ad --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2_pb.rb @@ -0,0 +1,77 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: ruby_generated_code_proto2.proto + +require 'google/protobuf' + +Google::Protobuf::DescriptorPool.generated_pool.build do + add_file("ruby_generated_code_proto2.proto", :syntax => :proto2) do + add_message "A.B.C.TestMessage" do + optional :optional_int32, :int32, 1, default: 1 + optional :optional_int64, :int64, 2, default: 2 + optional :optional_uint32, :uint32, 3, default: 3 + optional :optional_uint64, :uint64, 4, default: 4 + optional :optional_bool, :bool, 5, default: true + optional :optional_double, :double, 6, default: 6 + optional :optional_float, :float, 7, default: 7 + optional :optional_string, :string, 8, default: "default str" + optional :optional_bytes, :bytes, 9, default: "\x00\x01\x02\x40\x66\x75\x62\x61\x72".force_encoding("ASCII-8BIT") + optional :optional_enum, :enum, 10, "A.B.C.TestEnum", default: 1 + optional :optional_msg, :message, 11, "A.B.C.TestMessage" + repeated :repeated_int32, :int32, 21 + repeated :repeated_int64, :int64, 22 + repeated :repeated_uint32, :uint32, 23 + repeated :repeated_uint64, :uint64, 24 + repeated :repeated_bool, :bool, 25 + repeated :repeated_double, :double, 26 + repeated :repeated_float, :float, 27 + repeated :repeated_string, :string, 28 + repeated :repeated_bytes, :bytes, 29 + repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum" + repeated :repeated_msg, :message, 31, "A.B.C.TestMessage" + required :required_int32, :int32, 41 + required :required_int64, :int64, 42 + required :required_uint32, :uint32, 43 + required :required_uint64, :uint64, 44 + required :required_bool, :bool, 45 + required :required_double, :double, 46 + required :required_float, :float, 47 + required :required_string, :string, 48 + required :required_bytes, :bytes, 49 + required :required_enum, :enum, 50, "A.B.C.TestEnum" + required :required_msg, :message, 51, "A.B.C.TestMessage" + optional :nested_message, :message, 80, "A.B.C.TestMessage.NestedMessage" + oneof :my_oneof do + optional :oneof_int32, :int32, 61 + optional :oneof_int64, :int64, 62 + optional :oneof_uint32, :uint32, 63 + optional :oneof_uint64, :uint64, 64 + optional :oneof_bool, :bool, 65 + optional :oneof_double, :double, 66 + optional :oneof_float, :float, 67 + optional :oneof_string, :string, 68 + optional :oneof_bytes, :bytes, 69 + optional :oneof_enum, :enum, 70, "A.B.C.TestEnum" + optional :oneof_msg, :message, 71, "A.B.C.TestMessage" + end + end + add_message "A.B.C.TestMessage.NestedMessage" do + optional :foo, :int32, 1 + end + add_enum "A.B.C.TestEnum" do + value :Default, 0 + value :A, 1 + value :B, 2 + value :C, 3 + end + end +end + +module A + module B + module C + TestMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage").msgclass + TestMessage::NestedMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.NestedMessage").msgclass + TestEnum = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestEnum").enummodule + end + end +end diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc index bd737c02..6b9ea822 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc @@ -28,6 +28,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include <iomanip> #include <sstream> #include <google/protobuf/compiler/code_generator.h> @@ -45,12 +46,13 @@ namespace compiler { namespace ruby { // Forward decls. -std::string IntToString(int32 value); +template<class numeric_type> std::string NumberToString(numeric_type value); std::string GetRequireName(const std::string& proto_file); std::string LabelForField(google::protobuf::FieldDescriptor* field); std::string TypeName(google::protobuf::FieldDescriptor* field); -void GenerateMessage(const google::protobuf::Descriptor* message, - google::protobuf::io::Printer* printer); +bool GenerateMessage(const google::protobuf::Descriptor* message, + google::protobuf::io::Printer* printer, + std::string* error); void GenerateEnum(const google::protobuf::EnumDescriptor* en, google::protobuf::io::Printer* printer); void GenerateMessageAssignment( @@ -61,8 +63,11 @@ void GenerateEnumAssignment( const std::string& prefix, const google::protobuf::EnumDescriptor* en, google::protobuf::io::Printer* printer); - -std::string IntToString(int32 value) { +std::string DefaultValueForField( + const google::protobuf::FieldDescriptor* field); + +template<class numeric_type> +std::string NumberToString(numeric_type value) { std::ostringstream os; os << value; return os.str(); @@ -110,6 +115,62 @@ std::string TypeName(const google::protobuf::FieldDescriptor* field) { } } +string StringifySyntax(FileDescriptor::Syntax syntax) { + switch (syntax) { + case FileDescriptor::SYNTAX_PROTO2: + return "proto2"; + case FileDescriptor::SYNTAX_PROTO3: + return "proto3"; + case FileDescriptor::SYNTAX_UNKNOWN: + default: + GOOGLE_LOG(FATAL) << "Unsupported syntax; this generator only supports " + "proto2 and proto3 syntax."; + return ""; + } +} + +std::string DefaultValueForField(const google::protobuf::FieldDescriptor* field) { + switch(field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return NumberToString(field->default_value_int32()); + case FieldDescriptor::CPPTYPE_INT64: + return NumberToString(field->default_value_int64()); + case FieldDescriptor::CPPTYPE_UINT32: + return NumberToString(field->default_value_uint32()); + case FieldDescriptor::CPPTYPE_UINT64: + return NumberToString(field->default_value_uint64()); + case FieldDescriptor::CPPTYPE_FLOAT: + return NumberToString(field->default_value_float()); + case FieldDescriptor::CPPTYPE_DOUBLE: + return NumberToString(field->default_value_double()); + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + case FieldDescriptor::CPPTYPE_ENUM: + return NumberToString(field->default_value_enum()->number()); + case FieldDescriptor::CPPTYPE_STRING: { + std::ostringstream os; + string default_str = field->default_value_string(); + + if (field->type() == FieldDescriptor::TYPE_STRING) { + os << "\"" << default_str << "\""; + } else if (field->type() == FieldDescriptor::TYPE_BYTES) { + os << "\""; + + os.fill('0'); + for (int i = 0; i < default_str.length(); ++i) { + // Write the hex form of each byte. + os << "\\x" << std::hex << std::setw(2) + << ((uint16) ((unsigned char) default_str.at(i))); + } + os << "\".force_encoding(\"ASCII-8BIT\")"; + } + + return os.str(); + } + default: assert(false); return ""; + } +} + void GenerateField(const google::protobuf::FieldDescriptor* field, google::protobuf::io::Printer* printer) { @@ -124,7 +185,7 @@ void GenerateField(const google::protobuf::FieldDescriptor* field, "name", field->name(), "key_type", TypeName(key_field), "value_type", TypeName(value_field), - "number", IntToString(field->number())); + "number", NumberToString(field->number())); if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { printer->Print( @@ -146,19 +207,25 @@ void GenerateField(const google::protobuf::FieldDescriptor* field, printer->Print( ":$type$, $number$", "type", TypeName(field), - "number", IntToString(field->number())); + "number", NumberToString(field->number())); if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { printer->Print( - ", \"$subtype$\"\n", + ", \"$subtype$\"", "subtype", field->message_type()->full_name()); } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { printer->Print( - ", \"$subtype$\"\n", + ", \"$subtype$\"", "subtype", field->enum_type()->full_name()); - } else { - printer->Print("\n"); } + + if (field->has_default_value()) { + printer->Print( + ", default: $default$", + "default", DefaultValueForField(field)); + } + + printer->Print("\n"); } } @@ -178,13 +245,18 @@ void GenerateOneof(const google::protobuf::OneofDescriptor* oneof, printer->Print("end\n"); } -void GenerateMessage(const google::protobuf::Descriptor* message, - google::protobuf::io::Printer* printer) { - +bool GenerateMessage(const google::protobuf::Descriptor* message, + google::protobuf::io::Printer* printer, + std::string* error) { + if (message->extension_range_count() > 0 || message->extension_count() > 0) { + *error = "Extensions are not yet supported for proto2 .proto files."; + return false; + } + // Don't generate MapEntry messages -- we use the Ruby extension's native // support for map fields instead. if (message->options().map_entry()) { - return; + return true; } printer->Print( @@ -208,11 +280,15 @@ void GenerateMessage(const google::protobuf::Descriptor* message, printer->Print("end\n"); for (int i = 0; i < message->nested_type_count(); i++) { - GenerateMessage(message->nested_type(i), printer); + if (!GenerateMessage(message->nested_type(i), printer, error)) { + return false; + } } for (int i = 0; i < message->enum_type_count(); i++) { GenerateEnum(message->enum_type(i), printer); } + + return true; } void GenerateEnum(const google::protobuf::EnumDescriptor* en, @@ -227,7 +303,7 @@ void GenerateEnum(const google::protobuf::EnumDescriptor* en, printer->Print( "value :$name$, $number$\n", "name", value->name(), - "number", IntToString(value->number())); + "number", NumberToString(value->number())); } printer->Outdent(); @@ -423,7 +499,8 @@ bool MaybeEmitDependency(const FileDescriptor* import, const FileDescriptor* from, io::Printer* printer, string* error) { - if (import->syntax() == FileDescriptor::SYNTAX_PROTO2) { + if (from->syntax() == FileDescriptor::SYNTAX_PROTO3 && + import->syntax() == FileDescriptor::SYNTAX_PROTO2) { for (int i = 0; i < from->message_type_count(); i++) { if (UsesTypeFromFile(from->message_type(i), import, error)) { // Error text was already set by UsesTypeFromFile(). @@ -462,16 +539,29 @@ bool GenerateFile(const FileDescriptor* file, io::Printer* printer, } } - printer->Print( - "Google::Protobuf::DescriptorPool.generated_pool.build do\n"); + // TODO: Remove this when ruby supports extensions for proto2 syntax. + if (file->extension_count() > 0) { + *error = "Extensions are not yet supported for proto2 .proto files."; + return false; + } + + printer->Print("Google::Protobuf::DescriptorPool.generated_pool.build do\n"); + printer->Indent(); + printer->Print("add_file(\"$filename$\", :syntax => :$syntax$) do\n", + "filename", file->name(), "syntax", + StringifySyntax(file->syntax())); printer->Indent(); for (int i = 0; i < file->message_type_count(); i++) { - GenerateMessage(file->message_type(i), printer); + if (!GenerateMessage(file->message_type(i), printer, error)) { + return false; + } } for (int i = 0; i < file->enum_type_count(); i++) { GenerateEnum(file->enum_type(i), printer); } printer->Outdent(); + printer->Print("end\n"); + printer->Outdent(); printer->Print( "end\n\n"); @@ -492,10 +582,9 @@ bool Generator::Generate( GeneratorContext* generator_context, string* error) const { - if (file->syntax() != FileDescriptor::SYNTAX_PROTO3) { - *error = - "Can only generate Ruby code for proto3 .proto files.\n" - "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n"; + if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 && + file->syntax() != FileDescriptor::SYNTAX_PROTO2) { + *error = "Invalid or unsupported proto syntax"; return false; } diff --git a/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc index 1aabe8aa..984d6b89 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc @@ -56,7 +56,7 @@ string FindRubyTestDir() { // Some day, we may integrate build systems between protoc and the language // extensions to the point where we can do this test in a more automated way. -TEST(RubyGeneratorTest, GeneratorTest) { +TEST(RubyGeneratorTest, Proto3GeneratorTest) { string ruby_tests = FindRubyTestDir(); google::protobuf::compiler::CommandLineInterface cli; @@ -102,6 +102,52 @@ TEST(RubyGeneratorTest, GeneratorTest) { EXPECT_EQ(expected_output, output); } +TEST(RubyGeneratorTest, Proto2GeneratorTest) { + string ruby_tests = FindRubyTestDir(); + + google::protobuf::compiler::CommandLineInterface cli; + cli.SetInputsAreProtoPathRelative(true); + + ruby::Generator ruby_generator; + cli.RegisterGenerator("--ruby_out", &ruby_generator, ""); + + // Copy generated_code.proto to the temporary test directory. + string test_input; + GOOGLE_CHECK_OK(File::GetContents( + ruby_tests + "/ruby_generated_code_proto2.proto", + &test_input, + true)); + GOOGLE_CHECK_OK(File::SetContents( + TestTempDir() + "/ruby_generated_code_proto2.proto", + test_input, + true)); + + // Invoke the proto compiler (we will be inside TestTempDir() at this point). + string ruby_out = "--ruby_out=" + TestTempDir(); + string proto_path = "--proto_path=" + TestTempDir(); + const char* argv[] = { + "protoc", + ruby_out.c_str(), + proto_path.c_str(), + "ruby_generated_code_proto2.proto", + }; + + EXPECT_EQ(0, cli.Run(4, argv)); + + // Load the generated output and compare to the expected result. + string output; + GOOGLE_CHECK_OK(File::GetContents( + TestTempDir() + "/ruby_generated_code_proto2_pb.rb", + &output, + true)); + string expected_output; + GOOGLE_CHECK_OK(File::GetContents( + ruby_tests + "/ruby_generated_code_proto2_pb.rb", + &expected_output, + true)); + EXPECT_EQ(expected_output, output); +} + } // namespace } // namespace ruby } // namespace compiler |