Commit d0535cc0 authored by Harshit Chopra's avatar Harshit Chopra

Adds support for proto2 syntax for Ruby gem.

This change only adds basic proto2 support without advanced features
like extensions, custom options, maps, etc.

The protoc binary now generates ruby code for proto2 syntax.
However, for now, it is restricted to proto2 files without advanced features
like extensions, in which case it still errors out.

This change also modifies the DSL to add proto messages to the DescriptorPool.
There is a new DSL Builder#add_file to create a new FileDescriptor. With this,
the generated ruby DSL looks something like:

Google::Protobuf::DescriptorPool.generated_pool.build do
  add_file "test.proto" do
    add_message "foo" do
      optional :val, :int32, 1
    end
  end
end
parent 048f5c26
......@@ -171,9 +171,14 @@ js/testproto_libs2.js
# ruby test output
ruby/lib/
ruby/tests/basic_test_pb.rb
ruby/tests/basic_test_proto2_pb.rb
ruby/tests/generated_code_pb.rb
ruby/tests/test_import_pb.rb
ruby/tests/test_ruby_package_pb.rb
ruby/tests/generated_code_proto2_pb.rb
ruby/tests/test_import_proto2_pb.rb
ruby/tests/test_ruby_package_proto2_pb.rb
ruby/Gemfile.lock
ruby/compatibility_tests/v3.0.0/protoc
ruby/compatibility_tests/v3.0.0/tests/generated_code_pb.rb
......
......@@ -86,20 +86,45 @@ end
# Proto for tests.
genproto_output << "tests/generated_code.rb"
genproto_output << "tests/generated_code_proto2.rb"
genproto_output << "tests/test_import.rb"
genproto_output << "tests/test_import_proto2.rb"
genproto_output << "tests/test_ruby_package.rb"
genproto_output << "tests/test_ruby_package_proto2.rb"
genproto_output << "tests/basic_test.rb"
genproto_output << "tests/basic_test_proto2.rb"
file "tests/generated_code.rb" => "tests/generated_code.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/generated_code.proto"
end
file "tests/generated_code_proto2.rb" => "tests/generated_code_proto2.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/generated_code_proto2.proto"
end
file "tests/test_import.rb" => "tests/test_import.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/test_import.proto"
end
file "tests/test_import_proto2.rb" => "tests/test_import_proto2.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/test_import_proto2.proto"
end
file "tests/test_ruby_package.rb" => "tests/test_ruby_package.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/test_ruby_package.proto"
end
file "tests/test_ruby_package_proto2.rb" => "tests/test_ruby_package_proto2.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/test_ruby_package_proto2.proto"
end
file "tests/basic_test.rb" => "tests/basic_test.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/basic_test.proto"
end
file "tests/basic_test_proto2.rb" => "tests/basic_test_proto2.proto" do |file_task|
sh "../src/protoc --ruby_out=. tests/basic_test_proto2.proto"
end
task :genproto => genproto_output
task :clean do
......@@ -110,7 +135,7 @@ Gem::PackageTask.new(spec) do |pkg|
end
Rake::TestTask.new(:test => :build) do |t|
t.test_files = FileList["tests/*.rb"].exclude("tests/gc_test.rb")
t.test_files = FileList["tests/*.rb"].exclude("tests/gc_test.rb", "tests/common_tests.rb")
end
# gc_test needs to be split out to ensure the generated file hasn't been
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -91,12 +91,14 @@ void Init_protobuf_c() {
descriptor_instancevar_interned = rb_intern(kDescriptorInstanceVar);
DescriptorPool_register(protobuf);
Descriptor_register(protobuf);
FileDescriptor_register(protobuf);
FieldDescriptor_register(protobuf);
OneofDescriptor_register(protobuf);
EnumDescriptor_register(protobuf);
MessageBuilderContext_register(internal);
OneofBuilderContext_register(internal);
EnumBuilderContext_register(internal);
FileBuilderContext_register(internal);
Builder_register(internal);
RepeatedField_register(protobuf);
Map_register(protobuf);
......
......@@ -40,6 +40,7 @@
// Forward decls.
struct DescriptorPool;
struct Descriptor;
struct FileDescriptor;
struct FieldDescriptor;
struct EnumDescriptor;
struct MessageLayout;
......@@ -47,10 +48,12 @@ struct MessageField;
struct MessageHeader;
struct MessageBuilderContext;
struct EnumBuilderContext;
struct FileBuilderContext;
struct Builder;
typedef struct DescriptorPool DescriptorPool;
typedef struct Descriptor Descriptor;
typedef struct FileDescriptor FileDescriptor;
typedef struct FieldDescriptor FieldDescriptor;
typedef struct OneofDescriptor OneofDescriptor;
typedef struct EnumDescriptor EnumDescriptor;
......@@ -60,6 +63,7 @@ typedef struct MessageHeader MessageHeader;
typedef struct MessageBuilderContext MessageBuilderContext;
typedef struct OneofBuilderContext OneofBuilderContext;
typedef struct EnumBuilderContext EnumBuilderContext;
typedef struct FileBuilderContext FileBuilderContext;
typedef struct Builder Builder;
/*
......@@ -118,6 +122,10 @@ struct Descriptor {
const upb_handlers* json_serialize_handlers_preserve;
};
struct FileDescriptor {
const upb_filedef* filedef;
};
struct FieldDescriptor {
const upb_fielddef* fielddef;
};
......@@ -145,18 +153,27 @@ struct EnumBuilderContext {
VALUE enumdesc;
};
struct FileBuilderContext {
VALUE pending_list;
VALUE file_descriptor;
VALUE builder;
};
struct Builder {
VALUE pending_list;
VALUE default_file_descriptor;
upb_def** defs; // used only while finalizing
};
extern VALUE cDescriptorPool;
extern VALUE cDescriptor;
extern VALUE cFileDescriptor;
extern VALUE cFieldDescriptor;
extern VALUE cEnumDescriptor;
extern VALUE cMessageBuilderContext;
extern VALUE cOneofBuilderContext;
extern VALUE cEnumBuilderContext;
extern VALUE cFileBuilderContext;
extern VALUE cBuilder;
extern VALUE cError;
......@@ -175,7 +192,7 @@ VALUE DescriptorPool_alloc(VALUE klass);
void DescriptorPool_register(VALUE module);
DescriptorPool* ruby_to_DescriptorPool(VALUE value);
VALUE DescriptorPool_add(VALUE _self, VALUE def);
VALUE DescriptorPool_build(VALUE _self);
VALUE DescriptorPool_build(int argc, VALUE* argv, VALUE _self);
VALUE DescriptorPool_lookup(VALUE _self, VALUE name);
VALUE DescriptorPool_generated_pool(VALUE _self);
......@@ -184,6 +201,7 @@ void Descriptor_free(void* _self);
VALUE Descriptor_alloc(VALUE klass);
void Descriptor_register(VALUE module);
Descriptor* ruby_to_Descriptor(VALUE value);
VALUE Descriptor_initialize(VALUE _self, VALUE file_descriptor_rb);
VALUE Descriptor_name(VALUE _self);
VALUE Descriptor_name_set(VALUE _self, VALUE str);
VALUE Descriptor_each(VALUE _self);
......@@ -193,8 +211,19 @@ VALUE Descriptor_add_oneof(VALUE _self, VALUE obj);
VALUE Descriptor_each_oneof(VALUE _self);
VALUE Descriptor_lookup_oneof(VALUE _self, VALUE name);
VALUE Descriptor_msgclass(VALUE _self);
VALUE Descriptor_file_descriptor(VALUE _self);
extern const rb_data_type_t _Descriptor_type;
void FileDescriptor_mark(void* _self);
void FileDescriptor_free(void* _self);
VALUE FileDescriptor_alloc(VALUE klass);
void FileDescriptor_register(VALUE module);
FileDescriptor* ruby_to_FileDescriptor(VALUE value);
VALUE FileDescriptor_initialize(int argc, VALUE* argv, VALUE _self);
VALUE FileDescriptor_name(VALUE _self);
VALUE FileDescriptor_syntax(VALUE _self);
VALUE FileDescriptor_syntax_set(VALUE _self, VALUE syntax);
void FieldDescriptor_mark(void* _self);
void FieldDescriptor_free(void* _self);
VALUE FieldDescriptor_alloc(VALUE klass);
......@@ -204,6 +233,8 @@ VALUE FieldDescriptor_name(VALUE _self);
VALUE FieldDescriptor_name_set(VALUE _self, VALUE str);
VALUE FieldDescriptor_type(VALUE _self);
VALUE FieldDescriptor_type_set(VALUE _self, VALUE type);
VALUE FieldDescriptor_default(VALUE _self);
VALUE FieldDescriptor_default_set(VALUE _self, VALUE default_value);
VALUE FieldDescriptor_label(VALUE _self);
VALUE FieldDescriptor_label_set(VALUE _self, VALUE label);
VALUE FieldDescriptor_number(VALUE _self);
......@@ -211,6 +242,8 @@ VALUE FieldDescriptor_number_set(VALUE _self, VALUE number);
VALUE FieldDescriptor_submsg_name(VALUE _self);
VALUE FieldDescriptor_submsg_name_set(VALUE _self, VALUE value);
VALUE FieldDescriptor_subtype(VALUE _self);
VALUE FieldDescriptor_has(VALUE _self, VALUE msg_rb);
VALUE FieldDescriptor_clear(VALUE _self, VALUE msg_rb);
VALUE FieldDescriptor_get(VALUE _self, VALUE msg_rb);
VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value);
upb_fieldtype_t ruby_to_fieldtype(VALUE type);
......@@ -231,6 +264,8 @@ void EnumDescriptor_free(void* _self);
VALUE EnumDescriptor_alloc(VALUE klass);
void EnumDescriptor_register(VALUE module);
EnumDescriptor* ruby_to_EnumDescriptor(VALUE value);
VALUE EnumDescriptor_initialize(VALUE _self, VALUE file_descriptor_rb);
VALUE EnumDescriptor_file_descriptor(VALUE _self);
VALUE EnumDescriptor_name(VALUE _self);
VALUE EnumDescriptor_name_set(VALUE _self, VALUE str);
VALUE EnumDescriptor_add_value(VALUE _self, VALUE name, VALUE number);
......@@ -272,12 +307,23 @@ EnumBuilderContext* ruby_to_EnumBuilderContext(VALUE value);
VALUE EnumBuilderContext_initialize(VALUE _self, VALUE enumdesc);
VALUE EnumBuilderContext_value(VALUE _self, VALUE name, VALUE number);
void FileBuilderContext_mark(void* _self);
void FileBuilderContext_free(void* _self);
VALUE FileBuilderContext_alloc(VALUE klass);
void FileBuilderContext_register(VALUE module);
VALUE FileBuilderContext_initialize(VALUE _self, VALUE file_descriptor,
VALUE builder);
VALUE FileBuilderContext_add_message(VALUE _self, VALUE name);
VALUE FileBuilderContext_add_enum(VALUE _self, VALUE name);
VALUE FileBuilderContext_pending_descriptors(VALUE _self);
void Builder_mark(void* _self);
void Builder_free(void* _self);
VALUE Builder_alloc(VALUE klass);
void Builder_register(VALUE module);
Builder* ruby_to_Builder(VALUE value);
VALUE Builder_initialize(VALUE _self);
VALUE Builder_add_file(int argc, VALUE *argv, VALUE _self);
VALUE Builder_add_message(VALUE _self, VALUE name);
VALUE Builder_add_enum(VALUE _self, VALUE name);
VALUE Builder_finalize_to_pool(VALUE _self, VALUE pool_rb);
......@@ -443,10 +489,12 @@ VALUE Map_iter_value(Map_iter* iter);
// -----------------------------------------------------------------------------
#define MESSAGE_FIELD_NO_CASE ((size_t)-1)
#define MESSAGE_FIELD_NO_HASBIT ((size_t)-1)
struct MessageField {
size_t offset;
size_t case_offset; // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE.
size_t hasbit;
};
struct MessageLayout {
......@@ -457,6 +505,9 @@ struct MessageLayout {
MessageLayout* create_layout(const upb_msgdef* msgdef);
void free_layout(MessageLayout* layout);
bool field_contains_hasbit(MessageLayout* layout,
const upb_fielddef* field);
VALUE layout_get_default(const upb_fielddef* field);
VALUE layout_get(MessageLayout* layout,
const void* storage,
const upb_fielddef* field);
......@@ -464,6 +515,12 @@ void layout_set(MessageLayout* layout,
void* storage,
const upb_fielddef* field,
VALUE val);
VALUE layout_has(MessageLayout* layout,
const void* storage,
const upb_fielddef* field);
void layout_clear(MessageLayout* layout,
const void* storage,
const upb_fielddef* field);
void layout_init(MessageLayout* layout, void* storage);
void layout_mark(MessageLayout* layout, void* storage);
void layout_dup(MessageLayout* layout, void* to, void* from);
......
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/ruby
# basic_test_pb.rb is in the same directory as this test.
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
require 'basic_test_proto2_pb'
require 'common_tests'
require 'google/protobuf'
require 'json'
require 'test/unit'
# ------------- generated code --------------
module BasicTestProto2
pool = Google::Protobuf::DescriptorPool.new
pool.build do
add_file "test_proto2.proto", syntax: :proto2 do
add_message "BadFieldNames" do
optional :dup, :int32, 1
optional :class, :int32, 2
optional :"a.b", :int32, 3
end
end
end
BadFieldNames = pool.lookup("BadFieldNames").msgclass
# ------------ test cases ---------------
class MessageContainerTest < Test::Unit::TestCase
# Required by CommonTests module to resolve proto2 proto classes used in tests.
def proto_module
::BasicTestProto2
end
include CommonTests
def test_has_field
m = TestMessage.new
assert_false m.has_optional_int32?
assert_false TestMessage.descriptor.lookup('optional_int32').has?(m)
assert_false m.has_optional_int64?
assert_false TestMessage.descriptor.lookup('optional_int64').has?(m)
assert_false m.has_optional_uint32?
assert_false TestMessage.descriptor.lookup('optional_uint32').has?(m)
assert_false m.has_optional_uint64?
assert_false TestMessage.descriptor.lookup('optional_uint64').has?(m)
assert_false m.has_optional_bool?
assert_false TestMessage.descriptor.lookup('optional_bool').has?(m)
assert_false m.has_optional_float?
assert_false TestMessage.descriptor.lookup('optional_float').has?(m)
assert_false m.has_optional_double?
assert_false TestMessage.descriptor.lookup('optional_double').has?(m)
assert_false m.has_optional_string?
assert_false TestMessage.descriptor.lookup('optional_string').has?(m)
assert_false m.has_optional_bytes?
assert_false TestMessage.descriptor.lookup('optional_bytes').has?(m)
assert_false m.has_optional_enum?
assert_false TestMessage.descriptor.lookup('optional_enum').has?(m)
m = TestMessage.new(:optional_int32 => nil)
assert_false m.has_optional_int32?
assert_raise NoMethodError do
m.has_repeated_msg?
end
assert_raise ArgumentError do
TestMessage.descriptor.lookup('repeated_msg').has?(m)
end
m.optional_msg = TestMessage2.new
assert_true m.has_optional_msg?
assert_true TestMessage.descriptor.lookup('optional_msg').has?(m)
m = OneofMessage.new
assert_false m.has_my_oneof?
m.a = "foo"
assert_true m.has_a?
assert_true OneofMessage.descriptor.lookup('a').has?(m)
assert_equal "foo", m.a
assert_true m.has_my_oneof?
assert_false m.has_b?
assert_false OneofMessage.descriptor.lookup('b').has?(m)
assert_false m.has_c?
assert_false OneofMessage.descriptor.lookup('c').has?(m)
assert_false m.has_d?
assert_false OneofMessage.descriptor.lookup('d').has?(m)
m = OneofMessage.new
m.b = 100
assert_true m.has_b?
assert_equal 100, m.b
assert_true m.has_my_oneof?
assert_false m.has_a?
assert_false m.has_c?
assert_false m.has_d?
m = OneofMessage.new
m.c = TestMessage2.new
assert_true m.has_c?
assert_equal TestMessage2.new, m.c
assert_true m.has_my_oneof?
assert_false m.has_a?
assert_false m.has_b?
assert_false m.has_d?
m = OneofMessage.new
m.d = :A
assert_true m.has_d?
assert_equal :A, m.d
assert_true m.has_my_oneof?
assert_false m.has_a?
assert_false m.has_b?
assert_false m.has_c?
end
def test_defined_defaults
m = TestMessageDefaults.new
assert_equal 1, m.optional_int32
assert_equal 2, m.optional_int64
assert_equal 3, m.optional_uint32
assert_equal 4, m.optional_uint64
assert_equal true, m.optional_bool
assert_equal 6.0, m.optional_float
assert_equal 7.0, m.optional_double
assert_equal "Default Str", m.optional_string
assert_equal "\xCF\xA5s\xBD\xBA\xE6fubar".force_encoding("ASCII-8BIT"), m.optional_bytes
assert_equal :B2, m.optional_enum
assert_false m.has_optional_int32?
assert_false m.has_optional_int64?
assert_false m.has_optional_uint32?
assert_false m.has_optional_uint64?
assert_false m.has_optional_bool?
assert_false m.has_optional_float?
assert_false m.has_optional_double?
assert_false m.has_optional_string?
assert_false m.has_optional_bytes?
assert_false m.has_optional_enum?
end
def test_set_clear_defaults
m = TestMessageDefaults.new
m.optional_int32 = -42
assert_equal -42, m.optional_int32
assert_true m.has_optional_int32?
m.clear_optional_int32
assert_equal 1, m.optional_int32
assert_false m.has_optional_int32?
m.optional_string = "foo bar"
assert_equal "foo bar", m.optional_string
assert_true m.has_optional_string?
m.clear_optional_string
assert_equal "Default Str", m.optional_string
assert_false m.has_optional_string?
m.optional_msg = TestMessage2.new(:foo => 42)
assert_equal TestMessage2.new(:foo => 42), m.optional_msg
assert_true m.has_optional_msg?
m.clear_optional_msg
assert_equal nil, m.optional_msg
assert_false m.has_optional_msg?
m.optional_msg = TestMessage2.new(:foo => 42)
assert_equal TestMessage2.new(:foo => 42), m.optional_msg
assert_true TestMessageDefaults.descriptor.lookup('optional_msg').has?(m)
TestMessageDefaults.descriptor.lookup('optional_msg').clear(m)
assert_equal nil, m.optional_msg
assert_false TestMessageDefaults.descriptor.lookup('optional_msg').has?(m)
m = TestMessage.new
m.repeated_int32.push(1)
assert_equal [1], m.repeated_int32
m.clear_repeated_int32
assert_equal [], m.repeated_int32
m = OneofMessage.new
m.a = "foo"
assert_equal "foo", m.a
assert_true m.has_a?
m.clear_a
assert_false m.has_a?
m = OneofMessage.new
m.a = "foobar"
assert_true m.has_my_oneof?
m.clear_my_oneof
assert_false m.has_my_oneof?
m = OneofMessage.new
m.a = "bar"
assert_equal "bar", m.a
assert_true m.has_my_oneof?
OneofMessage.descriptor.lookup('a').clear(m)
assert_false m.has_my_oneof?
end
def test_initialization_map_errors
e = assert_raise ArgumentError do
TestMessage.new(:hello => "world")
end
assert_match(/hello/, e.message)
e = assert_raise ArgumentError do
TestMessage.new(:repeated_uint32 => "hello")
end
assert_equal e.message, "Expected array as initializer value for repeated field 'repeated_uint32'."
end
def test_to_h
m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2'])
expected_result = {
:optional_bool=>true,
:optional_double=>-10.100001,
:optional_string=>"foo",
:repeated_string=>["bar1", "bar2"],
}
assert_equal expected_result, m.to_h
m = OneofMessage.new(:a => "foo")
expected_result = {:a => "foo"}
assert_equal expected_result, m.to_h
end
def test_map_keyword_disabled
pool = Google::Protobuf::DescriptorPool.new
e = assert_raise ArgumentError do
pool.build do
add_file 'test_file.proto', syntax: :proto2 do
add_message "MapMessage" do
map :map_string_int32, :string, :int32, 1
map :map_string_msg, :string, :message, 2, "TestMessage2"
end
end
end
end
assert_match(/Cannot add a native map/, e.message)
end
def test_respond_to
# This test fails with JRuby 1.7.23, likely because of an old JRuby bug.
return if RUBY_PLATFORM == "java"
msg = TestMessage.new
assert !msg.respond_to?(:bacon)
end
def test_file_descriptor
file_descriptor = TestMessage.descriptor.file_descriptor
assert_true nil != file_descriptor
assert_equal "tests/basic_test_proto2.proto", file_descriptor.name
assert_equal :proto2, file_descriptor.syntax
file_descriptor = TestEnum.descriptor.file_descriptor
assert_true nil != file_descriptor
assert_equal "tests/basic_test_proto2.proto", file_descriptor.name
assert_equal :proto2, file_descriptor.syntax
end
end
end
syntax = "proto3";
package basic_test;
message Foo {
Bar bar = 1;
repeated Baz baz = 2;
}
message Bar {
string msg = 1;
}
message Baz {
string msg = 1;
}
message TestMessage {
int32 optional_int32 = 1;
int64 optional_int64 = 2;
uint32 optional_uint32 = 3;
uint64 optional_uint64 = 4;
bool optional_bool = 5;
float optional_float = 6;
double optional_double = 7;
string optional_string = 8;
bytes optional_bytes = 9;
TestMessage2 optional_msg = 10;
TestEnum optional_enum = 11;
repeated int32 repeated_int32 = 12;
repeated int64 repeated_int64 = 13;
repeated uint32 repeated_uint32 = 14;
repeated uint64 repeated_uint64 = 15;
repeated bool repeated_bool = 16;
repeated float repeated_float = 17;
repeated double repeated_double = 18;
repeated string repeated_string = 19;
repeated bytes repeated_bytes = 20;
repeated TestMessage2 repeated_msg = 21;
repeated TestEnum repeated_enum = 22;
}
message TestMessage2 {
int32 foo = 1;
}
enum TestEnum {
Default = 0;
A = 1;
B = 2;
C = 3;
}
message TestEmbeddedMessageParent {
TestEmbeddedMessageChild child_msg = 1;
int32 number = 2;
repeated TestEmbeddedMessageChild repeated_msg = 3;
repeated int32 repeated_number = 4;
}
message TestEmbeddedMessageChild {
TestMessage sub_child = 1;
}
message Recursive1 {
Recursive2 foo = 1;
}
message Recursive2 {
Recursive1 foo = 1;
}
message MapMessage {
map<string, int32> map_string_int32 = 1;
map<string, TestMessage2> map_string_msg = 2;
}
message MapMessageWireEquiv {
repeated MapMessageWireEquiv_entry1 map_string_int32 = 1;
repeated MapMessageWireEquiv_entry2 map_string_msg = 2;
}
message MapMessageWireEquiv_entry1 {
string key = 1;
int32 value = 2;
}
message MapMessageWireEquiv_entry2 {
string key = 1;
TestMessage2 value = 2;
}
message OneofMessage {
oneof my_oneof {
string a = 1;
int32 b = 2;
TestMessage2 c = 3;
TestEnum d = 4;
}
}
message Outer {
map<int32, Inner> items = 1;
}
message Inner {
}
\ No newline at end of file
syntax = "proto2";
package basic_test_proto2;
message Foo {
optional Bar bar = 1;
repeated Baz baz = 2;
}
message Bar {
optional string msg = 1;
}
message Baz {
optional string msg = 1;
}
message TestMessage {
optional int32 optional_int32 = 1;
optional int64 optional_int64 = 2;
optional uint32 optional_uint32 = 3;
optional uint64 optional_uint64 = 4;
optional bool optional_bool = 5;
optional float optional_float = 6;
optional double optional_double = 7;
optional string optional_string = 8;
optional bytes optional_bytes = 9;
optional TestMessage2 optional_msg = 10;
optional TestEnum optional_enum = 11;
repeated int32 repeated_int32 = 12;
repeated int64 repeated_int64 = 13;
repeated uint32 repeated_uint32 = 14;
repeated uint64 repeated_uint64 = 15;
repeated bool repeated_bool = 16;
repeated float repeated_float = 17;
repeated double repeated_double = 18;
repeated string repeated_string = 19;
repeated bytes repeated_bytes = 20;
repeated TestMessage2 repeated_msg = 21;
repeated TestEnum repeated_enum = 22;
}
message TestMessage2 {
optional int32 foo = 1;
}
message TestMessageDefaults {
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 float optional_float = 6 [default = 6];
optional double optional_double = 7 [default = 7];
optional string optional_string = 8 [default = "Default Str"];
optional bytes optional_bytes = 9 [default = "\xCF\xA5s\xBD\xBA\xE6fubar"];
optional TestMessage2 optional_msg = 10;
optional TestNonZeroEnum optional_enum = 11 [default = B2];
}
enum TestEnum {
Default = 0;
A = 1;
B = 2;
C = 3;
}
enum TestNonZeroEnum {
A2 = 1;
B2 = 2;
C2 = 3;
}
message TestEmbeddedMessageParent {
optional TestEmbeddedMessageChild child_msg = 1;
optional int32 number = 2;
repeated TestEmbeddedMessageChild repeated_msg = 3;
repeated int32 repeated_number = 4;
}
message TestEmbeddedMessageChild {
optional TestMessage sub_child = 1;
}
message Recursive1 {
optional Recursive2 foo = 1;
}
message Recursive2 {
optional Recursive1 foo = 1;
}
message MapMessageWireEquiv {
repeated MapMessageWireEquiv_entry1 map_string_int32 = 1;
repeated MapMessageWireEquiv_entry2 map_string_msg = 2;
}
message MapMessageWireEquiv_entry1 {
optional string key = 1;
optional int32 value = 2;
}
message MapMessageWireEquiv_entry2 {
optional string key = 1;
optional TestMessage2 value = 2;
}
message OneofMessage {
oneof my_oneof {
string a = 1;
int32 b = 2;
TestMessage2 c = 3;
TestEnum d = 4;
}
}
This diff is collapsed.
......@@ -6,12 +6,13 @@ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
old_gc = GC.stress
GC.stress = 0x01 | 0x04
require 'generated_code_pb'
require 'generated_code_proto2_pb'
GC.stress = old_gc
require 'test/unit'
class GCTest < Test::Unit::TestCase
def get_msg
def get_msg_proto3
A::B::C::TestMessage.new(
:optional_int32 => 1,
:optional_int64 => 1,
......@@ -46,12 +47,55 @@ class GCTest < Test::Unit::TestCase
:map_string_bool => {"a" => true},
)
end
def get_msg_proto2
A::B::Proto2::TestMessage.new(
:optional_int32 => 1,
:optional_int64 => 1,
:optional_uint32 => 1,
:optional_uint64 => 1,
:optional_bool => true,
:optional_double => 1.0,
:optional_float => 1.0,
:optional_string => "a",
:optional_bytes => "b",
:optional_enum => A::B::Proto2::TestEnum::A,
:optional_msg => A::B::Proto2::TestMessage.new(),
:repeated_int32 => [1],
:repeated_int64 => [1],
:repeated_uint32 => [1],
:repeated_uint64 => [1],
:repeated_bool => [true],
:repeated_double => [1.0],
:repeated_float => [1.0],
:repeated_string => ["a"],
:repeated_bytes => ["b"],
:repeated_enum => [A::B::Proto2::TestEnum::A],
:repeated_msg => [A::B::Proto2::TestMessage.new()],
:required_int32 => 1,
:required_int64 => 1,
:required_uint32 => 1,
:required_uint64 => 1,
:required_bool => true,
:required_double => 1.0,
:required_float => 1.0,
:required_string => "a",
:required_bytes => "b",
:required_enum => A::B::Proto2::TestEnum::A,
:required_msg => A::B::Proto2::TestMessage.new(),
)
end
def test_generated_msg
old_gc = GC.stress
GC.stress = 0x01 | 0x04
from = get_msg
from = get_msg_proto3
data = A::B::C::TestMessage.encode(from)
to = A::B::C::TestMessage.decode(data)
from = get_msg_proto2
data = A::B::Proto2::TestMessage.encode(from)
to = A::B::Proto2::TestMessage.decode(data)
GC.stress = old_gc
puts "passed"
end
......
syntax = "proto2";
package a.b.proto2;
message TestMessage {
optional int32 optional_int32 = 1;
optional int64 optional_int64 = 2;
optional uint32 optional_uint32 = 3;
optional uint64 optional_uint64 = 4;
optional bool optional_bool = 5;
optional double optional_double = 6;
optional float optional_float = 7;
optional string optional_string = 8;
optional bytes optional_bytes = 9;
optional TestEnum optional_enum = 10;
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;
// Reserved for non-existing field test.
// int32 non_exist = 89;
}
enum TestEnum {
Default = 0;
A = 1;
B = 2;
C = 3;
}
message TestUnknown {
optional TestUnknown optional_unknown = 11;
repeated TestUnknown repeated_unknown = 31;
oneof my_oneof {
TestUnknown oneof_unknown = 51;
}
optional int32 unknown_field = 89;
}
#!/usr/bin/ruby
# generated_code.rb is in the same directory as this test.
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
require 'generated_code_proto2_pb'
require 'test_import_proto2_pb'
require 'test_ruby_package_proto2_pb'
require 'test/unit'
class GeneratedCodeProto2Test < Test::Unit::TestCase
def test_generated_msg
# just test that we can instantiate the message. The purpose of this test
# is to ensure that the output of the code generator is valid Ruby and
# successfully creates message definitions and classes, not to test every
# aspect of the extension (basic.rb is for that).
m = A::B::Proto2::TestMessage.new()
m2 = FooBar::Proto2::TestImportedMessage.new()
m3 = A::B::Proto2::TestRubyPackageMessage.new()
end
end
syntax = "proto2";
package foo_bar.proto2;
message TestImportedMessage {}
syntax = "proto2";
package foo_bar_proto2;
option ruby_package = "A.B.Proto2";
message TestRubyPackageMessage {}
......@@ -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
......
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;
}
# 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
......@@ -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;
}
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment