From 71e8dca08312d9f9d76db6c2f762f8b395d05226 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Wed, 30 Mar 2016 09:42:37 +0100 Subject: [PATCH 1/8] Refactoring of FieldDescriptor This makes no externally visible behavioral changes. Internally and non-behaviorally: - We use a field (compiler-generated) to store the JsonName to avoid recomputing it repeatedly - The documentation for JsonName is updated to reflect the meaning better - Readonly autoprops and expression-bodied properties used where possible --- .../Reflection/FieldDescriptor.cs | 100 +++++++----------- 1 file changed, 39 insertions(+), 61 deletions(-) diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs index 6083f1719..de6e57176 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs @@ -30,9 +30,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion -using System; -using System.Linq; using Google.Protobuf.Compatibility; +using System; namespace Google.Protobuf.Reflection { @@ -41,20 +40,35 @@ namespace Google.Protobuf.Reflection /// public sealed class FieldDescriptor : DescriptorBase, IComparable { - private readonly FieldDescriptorProto proto; private EnumDescriptor enumType; private MessageDescriptor messageType; - private readonly MessageDescriptor containingType; - private readonly OneofDescriptor containingOneof; private FieldType fieldType; private readonly string propertyName; // Annoyingly, needed in Crosslink. private IFieldAccessor accessor; + /// + /// Get the field's containing message type. + /// + public MessageDescriptor ContainingType { get; } + + /// + /// Returns the oneof containing this field, or null if it is not part of a oneof. + /// + public OneofDescriptor ContainingOneof { get; } + + /// + /// The effective JSON name for this field. This is usually the lower-camel-cased form of the field name, + /// but can be overridden using the json_name option in the .proto file. + /// + public string JsonName { get; } + + internal FieldDescriptorProto Proto { get; } + internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, string propertyName) : base(file, file.ComputeFullName(parent, proto.Name), index) { - this.proto = proto; + Proto = proto; if (proto.Type != 0) { fieldType = GetFieldTypeFromProtoType(proto.Type); @@ -64,7 +78,7 @@ namespace Google.Protobuf.Reflection { throw new DescriptorValidationException(this, "Field numbers must be positive integers."); } - containingType = parent; + ContainingType = parent; // OneofIndex "defaults" to -1 due to a hack in FieldDescriptor.OnConstruction. if (proto.OneofIndex != -1) { @@ -73,7 +87,7 @@ namespace Google.Protobuf.Reflection throw new DescriptorValidationException(this, $"FieldDescriptorProto.oneof_index is out of range for type {parent.Name}"); } - containingOneof = parent.Oneofs[proto.OneofIndex]; + ContainingOneof = parent.Oneofs[proto.OneofIndex]; } file.DescriptorPool.AddSymbol(this); @@ -83,20 +97,14 @@ namespace Google.Protobuf.Reflection // We could trust the generated code and check whether the type of the property is // a MapField, but that feels a tad nasty. this.propertyName = propertyName; + JsonName = Proto.JsonName == "" ? JsonFormatter.ToCamelCase(Proto.Name) : Proto.JsonName; } + /// /// The brief name of the descriptor's target. /// - public override string Name { get { return proto.Name; } } - - - /// - /// The json_name option of the descriptor's target. - /// - public string JsonName { get { return proto.JsonName == "" ? JsonFormatter.ToCamelCase(proto.Name) : proto.JsonName; } } - - internal FieldDescriptorProto Proto { get { return proto; } } + public override string Name => Proto.Name; /// /// Returns the accessor for this field. @@ -116,7 +124,7 @@ namespace Google.Protobuf.Reflection /// and this property will return null. /// /// - public IFieldAccessor Accessor { get { return accessor; } } + public IFieldAccessor Accessor => accessor; /// /// Maps a field type as included in the .proto file to a FieldType. @@ -169,62 +177,32 @@ namespace Google.Protobuf.Reflection /// /// Returns true if this field is a repeated field; false otherwise. /// - public bool IsRepeated - { - get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; } - } + public bool IsRepeated => Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; /// /// Returns true if this field is a map field; false otherwise. /// - public bool IsMap - { - get { return fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; } - } + public bool IsMap => fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; /// /// Returns true if this field is a packed, repeated field; false otherwise. /// - public bool IsPacked - { + public bool IsPacked => // Note the || rather than && here - we're effectively defaulting to packed, because that *is* // the default in proto3, which is all we support. We may give the wrong result for the protos // within descriptor.proto, but that's okay, as they're never exposed and we don't use IsPacked // within the runtime. - get { return Proto.Options == null || Proto.Options.Packed; } - } - - /// - /// Get the field's containing message type. - /// - public MessageDescriptor ContainingType - { - get { return containingType; } - } - - /// - /// Returns the oneof containing this field, or null if it is not part of a oneof. - /// - public OneofDescriptor ContainingOneof - { - get { return containingOneof; } - } - + Proto.Options == null || Proto.Options.Packed; + /// /// Returns the type of the field. /// - public FieldType FieldType - { - get { return fieldType; } - } + public FieldType FieldType => fieldType; /// /// Returns the field number declared in the proto file. /// - public int FieldNumber - { - get { return Proto.Number; } - } + public int FieldNumber => Proto.Number; /// /// Compares this descriptor with another one, ordering in "canonical" order @@ -234,7 +212,7 @@ namespace Google.Protobuf.Reflection /// public int CompareTo(FieldDescriptor other) { - if (other.containingType != containingType) + if (other.ContainingType != ContainingType) { throw new ArgumentException("FieldDescriptors can only be compared to other FieldDescriptors " + "for fields of the same message type."); @@ -337,14 +315,14 @@ namespace Google.Protobuf.Reflection File.DescriptorPool.AddFieldByNumber(this); - if (containingType != null && containingType.Proto.Options != null && containingType.Proto.Options.MessageSetWireFormat) + if (ContainingType != null && ContainingType.Proto.Options != null && ContainingType.Proto.Options.MessageSetWireFormat) { throw new DescriptorValidationException(this, "MessageSet format is not supported."); } - accessor = CreateAccessor(propertyName); + accessor = CreateAccessor(); } - private IFieldAccessor CreateAccessor(string propertyName) + private IFieldAccessor CreateAccessor() { // If we're given no property name, that's because we really don't want an accessor. // (At the moment, that means it's a map entry message...) @@ -352,10 +330,10 @@ namespace Google.Protobuf.Reflection { return null; } - var property = containingType.ClrType.GetProperty(propertyName); + var property = ContainingType.ClrType.GetProperty(propertyName); if (property == null) { - throw new DescriptorValidationException(this, $"Property {propertyName} not found in {containingType.ClrType}"); + throw new DescriptorValidationException(this, $"Property {propertyName} not found in {ContainingType.ClrType}"); } return IsMap ? new MapFieldAccessor(property, this) : IsRepeated ? new RepeatedFieldAccessor(property, this) From 955841e620e2a70532c9ba6312e41676f1c92e6c Mon Sep 17 00:00:00 2001 From: Adam Michalik Date: Wed, 30 Mar 2016 12:54:40 -0700 Subject: [PATCH 2/8] Replace #include with #include iostream is not actually necessary here, and it introduces unnecessary static initializers. --- src/google/protobuf/stubs/int128.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/google/protobuf/stubs/int128.cc b/src/google/protobuf/stubs/int128.cc index d80c64f23..3a36b4b10 100644 --- a/src/google/protobuf/stubs/int128.cc +++ b/src/google/protobuf/stubs/int128.cc @@ -31,7 +31,7 @@ #include #include -#include // NOLINT(readability/streams) +#include // NOLINT(readability/streams) #include namespace google { From ca0461c1863bff4f9d33dcd352c66dc12d969560 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 31 Mar 2016 07:12:17 +0100 Subject: [PATCH 3/8] Introduce a new nuget package, Google.Protobuf.Tools, basically to contain protoc on multiple platforms. I've moved both protoc.exe and the proto files out of Google.Protobuf. The .proto files aren't a slam-dunk, but it feels like they belong with protoc as you'd *use* them with protoc. It's not clear to me whether we really need both an x86 and x64 version of protoc.exe, as x86 would work on 64-bit Windows anyway. Discuss :) --- csharp/Google.Protobuf.Tools.nuspec | 38 +++++++++++++++++++ .../Google.Protobuf/Google.Protobuf.nuspec | 14 ------- 2 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 csharp/Google.Protobuf.Tools.nuspec diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec new file mode 100644 index 000000000..4b008b6fd --- /dev/null +++ b/csharp/Google.Protobuf.Tools.nuspec @@ -0,0 +1,38 @@ + + + + Google.Protobuf.Tools + Google Protocol Buffers tools + Tools for Protocol Buffers - Google's data interchange format. + See project site for more info. + 3.0.0-beta2 + Google Inc. + protobuf-packages + https://github.com/google/protobuf/blob/master/LICENSE + https://github.com/google/protobuf + false + Tools for Protocol Buffers + Copyright 2015, Google Inc. + Protocol Buffers Binary Serialization Format Google proto proto3 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.nuspec b/csharp/src/Google.Protobuf/Google.Protobuf.nuspec index d5302544c..f51bc89a6 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.nuspec +++ b/csharp/src/Google.Protobuf/Google.Protobuf.nuspec @@ -23,7 +23,6 @@ - @@ -49,18 +48,5 @@ - - - - - - - - - - - - - \ No newline at end of file From dfd47600d1d197024a5c346a88cc3bfc52218c94 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 31 Mar 2016 10:46:55 +0100 Subject: [PATCH 4/8] Remove duplicate line --- csharp/Google.Protobuf.Tools.nuspec | 1 - 1 file changed, 1 deletion(-) diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec index 4b008b6fd..2b66b081e 100644 --- a/csharp/Google.Protobuf.Tools.nuspec +++ b/csharp/Google.Protobuf.Tools.nuspec @@ -31,7 +31,6 @@ - From 1283625b0f0b395a59d6d72b06bc7a62479b2d26 Mon Sep 17 00:00:00 2001 From: Manjunath Kudlur Date: Thu, 31 Mar 2016 09:53:00 -0700 Subject: [PATCH 5/8] Added an API to allow oversize protos when using C++ extension in Python --- .../google/protobuf/internal/message_test.py | 59 +++++++++++++++++++ python/google/protobuf/pyext/message.cc | 46 +++++++++++---- 2 files changed, 94 insertions(+), 11 deletions(-) diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py index d03f2d25d..ee6b944a8 100755 --- a/python/google/protobuf/internal/message_test.py +++ b/python/google/protobuf/internal/message_test.py @@ -57,7 +57,11 @@ try: except ImportError: import unittest from google.protobuf.internal import _parameterized +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor_pool from google.protobuf import map_unittest_pb2 +from google.protobuf import message_factory +from google.protobuf import text_format from google.protobuf import unittest_pb2 from google.protobuf import unittest_proto3_arena_pb2 from google.protobuf.internal import any_test_pb2 @@ -1776,5 +1780,60 @@ class PackedFieldTest(unittest.TestCase): b'\x70\x01') self.assertEqual(golden_data, message.SerializeToString()) + +@unittest.skipIf(api_implementation.Type() != 'cpp', + 'explicit tests of the C++ implementation') +class OversizeProtosTest(unittest.TestCase): + + def setUp(self): + self.file_desc = """ + name: "f/f.msg2" + package: "f" + message_type { + name: "msg1" + field { + name: "payload" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_STRING + } + } + message_type { + name: "msg2" + field { + name: "field" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: "msg1" + } + } + """ + pool = descriptor_pool.DescriptorPool() + desc = descriptor_pb2.FileDescriptorProto() + text_format.Parse(self.file_desc, desc) + pool.Add(desc) + self.proto_cls = message_factory.MessageFactory(pool).GetPrototype( + pool.FindMessageTypeByName('f.msg2')) + self.p = self.proto_cls() + self.p.field.payload = 'c' * (1024 * 1024 * 64 + 1) + self.p_serialized = self.p.SerializeToString() + + def testAssertOversizeProto(self): + from google.protobuf.pyext._message import SetAllowOversizeProtos + SetAllowOversizeProtos(False) + q = self.proto_cls() + try: + q.ParseFromString(self.p_serialized) + except message.DecodeError as e: + self.assertEqual(str(e), 'Error parsing message') + + def testSucceedOversizeProto(self): + from google.protobuf.pyext._message import SetAllowOversizeProtos + SetAllowOversizeProtos(True) + q = self.proto_cls() + q.ParseFromString(self.p_serialized) + self.assertEqual(self.p.field.payload, q.field.payload) + if __name__ == '__main__': unittest.main() diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc index 60ec9c1b9..7d3e8c370 100644 --- a/python/google/protobuf/pyext/message.cc +++ b/python/google/protobuf/pyext/message.cc @@ -1911,6 +1911,30 @@ static PyObject* CopyFrom(CMessage* self, PyObject* arg) { Py_RETURN_NONE; } +// Protobuf has a 64MB limit built in, this variable will override this. Please +// do not enable this unless you fully understand the implications: protobufs +// must all be kept in memory at the same time, so if they grow too big you may +// get OOM errors. The protobuf APIs do not provide any tools for processing +// protobufs in chunks. If you have protos this big you should break them up if +// it is at all convenient to do so. +static bool allow_oversize_protos = false; + +// Provide a method in the module to set allow_oversize_protos to a boolean +// value. This method returns the newly value of allow_oversize_protos. +static PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg) { + if (!arg || !PyBool_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "Argument to SetAllowOversizeProtos must be boolean"); + return NULL; + } + allow_oversize_protos = PyObject_IsTrue(arg); + if (allow_oversize_protos) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + static PyObject* MergeFromString(CMessage* self, PyObject* arg) { const void* data; Py_ssize_t data_length; @@ -1921,15 +1945,9 @@ static PyObject* MergeFromString(CMessage* self, PyObject* arg) { AssureWritable(self); io::CodedInputStream input( reinterpret_cast(data), data_length); -#if PROTOBUF_PYTHON_ALLOW_OVERSIZE_PROTOS - // Protobuf has a 64MB limit built in, this code will override this. Please do - // not enable this unless you fully understand the implications: protobufs - // must all be kept in memory at the same time, so if they grow too big you - // may get OOM errors. The protobuf APIs do not provide any tools for - // processing protobufs in chunks. If you have protos this big you should - // break them up if it is at all convenient to do so. - input.SetTotalBytesLimit(INT_MAX, INT_MAX); -#endif // PROTOBUF_PYTHON_ALLOW_OVERSIZE_PROTOS + if (allow_oversize_protos) { + input.SetTotalBytesLimit(INT_MAX, INT_MAX); + } PyDescriptorPool* pool = GetDescriptorPoolForMessage(self); input.SetExtensionRegistry(pool->pool, pool->message_factory); bool success = self->message->MergePartialFromCodedStream(&input); @@ -3046,6 +3064,11 @@ bool InitProto2MessageModule(PyObject *m) { } // namespace python } // namespace protobuf +static PyMethodDef ModuleMethods[] = { + {"SetAllowOversizeProtos", + (PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos, + METH_O, "Enable/disable oversize proto parsing."}, +}; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef _module = { @@ -3053,7 +3076,7 @@ static struct PyModuleDef _module = { "_message", google::protobuf::python::module_docstring, -1, - NULL, + ModuleMethods, /* m_methods */ NULL, NULL, NULL, @@ -3072,7 +3095,8 @@ extern "C" { #if PY_MAJOR_VERSION >= 3 m = PyModule_Create(&_module); #else - m = Py_InitModule3("_message", NULL, google::protobuf::python::module_docstring); + m = Py_InitModule3("_message", ModuleMethods, + google::protobuf::python::module_docstring); #endif if (m == NULL) { return INITFUNC_ERRORVAL; From 46e088e2b6ec4af99065fd9407c7b767cd3da0a9 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Fri, 1 Apr 2016 17:17:49 +0100 Subject: [PATCH 6/8] Remove duplicate test cases. (NCrunch noticed these.) --- csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs | 1 - csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs index 42455043e..344d727b9 100644 --- a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs +++ b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs @@ -321,7 +321,6 @@ namespace Google.Protobuf [TestCase("1970-01-01T00:00:00.001Z", 1000000)] [TestCase("1970-01-01T00:00:00.010Z", 10000000)] [TestCase("1970-01-01T00:00:00.100Z", 100000000)] - [TestCase("1970-01-01T00:00:00.100Z", 100000000)] [TestCase("1970-01-01T00:00:00.120Z", 120000000)] [TestCase("1970-01-01T00:00:00.123Z", 123000000)] [TestCase("1970-01-01T00:00:00.123400Z", 123400000)] diff --git a/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs b/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs index a0a622276..527ab3361 100644 --- a/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs +++ b/csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs @@ -249,7 +249,6 @@ namespace Google.Protobuf [TestCase("[,", 1)] [TestCase("{", 1)] [TestCase("{,", 1)] - [TestCase("{", 1)] [TestCase("{[", 1)] [TestCase("{{", 1)] [TestCase("{0", 1)] From 331cee502296b382ae42b499e2daab59488f91f8 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Fri, 1 Apr 2016 12:26:15 -0400 Subject: [PATCH 7/8] Add -position and -isAtEnd for use when manually parsing input streams. --- objectivec/GPBCodedInputStream.h | 9 +++++++++ objectivec/GPBCodedInputStream.m | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/objectivec/GPBCodedInputStream.h b/objectivec/GPBCodedInputStream.h index 061988832..d64b64e3f 100644 --- a/objectivec/GPBCodedInputStream.h +++ b/objectivec/GPBCodedInputStream.h @@ -109,6 +109,15 @@ NS_ASSUME_NONNULL_BEGIN /// or until an endgroup tag, whichever comes first. - (void)skipMessage; +/// Check to see if the logical end of the stream has been reached. +/// +/// This can return NO when there is no more data, but the current parsing +/// expected more data. +- (BOOL)isAtEnd; + +/// The offset into the stream. +- (size_t)position; + /// Verifies that the last call to @c -readTag returned the given tag value. /// This is used to verify that a nested group ended with the correct end tag. /// Throws @c NSParseErrorException if value does not match the last tag. diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m index fd8778383..eaa28e50b 100644 --- a/objectivec/GPBCodedInputStream.m +++ b/objectivec/GPBCodedInputStream.m @@ -359,6 +359,14 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state, } } +- (BOOL)isAtEnd { + return GPBCodedInputStreamIsAtEnd(&state_); +} + +- (size_t)position { + return state_.bufferPos; +} + - (double)readDouble { return GPBCodedInputStreamReadDouble(&state_); } From cf828deb9b5537c294e4a5628ef04fdbdfdcbb28 Mon Sep 17 00:00:00 2001 From: Manjunath Kudlur Date: Fri, 25 Mar 2016 10:58:46 -0700 Subject: [PATCH 8/8] Linking the cpp implementation extension statically with libprotobuf --- python/README.md | 12 ++---------- python/setup.py | 35 ++++++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/python/README.md b/python/README.md index 1b5b9dffd..57acfd94d 100644 --- a/python/README.md +++ b/python/README.md @@ -123,13 +123,5 @@ C++ Implementation The C++ implementation for Python messages is built as a Python extension to improve the overall protobuf Python performance. -To use the C++ implementation, you need to: -1) Install the C++ protobuf runtime library, please see instructions in the - parent directory. -2) Export an environment variable: - - $ export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp - -You must set this variable at runtime, before running your program, otherwise -the pure-Python implementation will be used. In a future release, we will -change the default so that C++ implementation is used whenever it is available. +To use the C++ implementation, you need to install the C++ protobuf runtime +library, please see instructions in the parent directory. diff --git a/python/setup.py b/python/setup.py index 6ea3bad73..f5c00a72d 100755 --- a/python/setup.py +++ b/python/setup.py @@ -157,13 +157,28 @@ class test_conformance(_build_py): status = subprocess.check_call(cmd, shell=True) +def get_option_from_sys_argv(option_str): + if option_str in sys.argv: + sys.argv.remove(option_str) + return True + return False + + if __name__ == '__main__': ext_module_list = [] - cpp_impl = '--cpp_implementation' warnings_as_errors = '--warnings_as_errors' - if cpp_impl in sys.argv: - sys.argv.remove(cpp_impl) + if get_option_from_sys_argv('--cpp_implementation'): + # Link libprotobuf.a and libprotobuf-lite.a statically with the + # extension. Note that those libraries have to be compiled with + # -fPIC for this to work. + compile_static_ext = get_option_from_sys_argv('--compile_static_extension') extra_compile_args = ['-Wno-write-strings', '-Wno-invalid-offsetof'] + libraries = ['protobuf'] + extra_objects = None + if compile_static_ext: + libraries = None + extra_objects = ['../src/.libs/libprotobuf.a', + '../src/.libs/libprotobuf-lite.a'] test_conformance.target = 'test_python_cpp' if "clang" in os.popen('$CC --version 2> /dev/null').read(): @@ -174,16 +189,22 @@ if __name__ == '__main__': sys.argv.remove(warnings_as_errors) # C++ implementation extension - ext_module_list.append( + ext_module_list.extend([ Extension( "google.protobuf.pyext._message", glob.glob('google/protobuf/pyext/*.cc'), include_dirs=[".", "../src"], - libraries=['protobuf'], + libraries=libraries, + extra_objects=extra_objects, library_dirs=['../src/.libs'], extra_compile_args=extra_compile_args, - ) - ) + ), + Extension( + "google.protobuf.internal._api_implementation", + glob.glob('google/protobuf/internal/api_implementation.cc'), + extra_compile_args=['-DPYTHON_PROTO2_CPP_IMPL_V2'], + ), + ]) os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp' # Keep this list of dependencies in sync with tox.ini.