15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#! /usr/bin/python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See README for usage instructions.
4ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochimport sys
5ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochimport os
6ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochimport subprocess
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We must use setuptools, not distutils, because we need to use the
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# namespace_packages option for the "google" package.
10ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochtry:
11ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  from setuptools import setup, Extension
12ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochexcept ImportError:
13ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  try:
14ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    from ez_setup import use_setuptools
15ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    use_setuptools()
16ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    from setuptools import setup, Extension
17ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  except ImportError:
18ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    sys.stderr.write(
19ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        "Could not import setuptools; make sure you have setuptools or "
20ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        "ez_setup installed.\n")
21ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    raise
22ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochfrom distutils.command.clean import clean as _clean
23ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochfrom distutils.command.build_py import build_py as _build_py
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from distutils.spawn import find_executable
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)maintainer_email = "protobuf@googlegroups.com"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Find the Protocol Compiler.
293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']):
303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  protoc = os.environ['PROTOC']
313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)elif os.path.exists("../src/protoc"):
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protoc = "../src/protoc"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif os.path.exists("../src/protoc.exe"):
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protoc = "../src/protoc.exe"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif os.path.exists("../vsprojects/Debug/protoc.exe"):
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protoc = "../vsprojects/Debug/protoc.exe"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)elif os.path.exists("../vsprojects/Release/protoc.exe"):
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protoc = "../vsprojects/Release/protoc.exe"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)else:
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protoc = find_executable("protoc")
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def generate_proto(source):
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Invokes the Protocol Compiler to generate a _pb2.py from the given
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  .proto file.  Does nothing if the output already exists and is newer than
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the input."""
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  output = source.replace(".proto", "_pb2.py").replace("../src/", "")
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (not os.path.exists(output) or
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (os.path.exists(source) and
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       os.path.getmtime(source) > os.path.getmtime(output))):
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print "Generating %s..." % output
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
54ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if not os.path.exists(source):
55ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      sys.stderr.write("Can't find required file: %s\n" % source)
56ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      sys.exit(-1)
57ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if protoc == None:
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sys.stderr.write(
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "protoc is not installed nor found in ../src.  Please compile it "
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "or install the binary package.\n")
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sys.exit(-1)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    protoc_command = [ protoc, "-I../src", "-I.", "--python_out=.", source ]
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if subprocess.call(protoc_command) != 0:
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sys.exit(-1)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
68ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochdef GenerateUnittestProtos():
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  generate_proto("../src/google/protobuf/unittest.proto")
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  generate_proto("../src/google/protobuf/unittest_custom_options.proto")
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  generate_proto("../src/google/protobuf/unittest_import.proto")
72ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  generate_proto("../src/google/protobuf/unittest_import_public.proto")
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  generate_proto("../src/google/protobuf/unittest_mset.proto")
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  generate_proto("../src/google/protobuf/unittest_no_generic_services.proto")
75ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  generate_proto("google/protobuf/internal/test_bad_identifiers.proto")
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  generate_proto("google/protobuf/internal/more_extensions.proto")
77ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  generate_proto("google/protobuf/internal/more_extensions_dynamic.proto")
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  generate_proto("google/protobuf/internal/more_messages.proto")
79ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  generate_proto("google/protobuf/internal/factory_test1.proto")
80ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  generate_proto("google/protobuf/internal/factory_test2.proto")
81ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
82ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochdef MakeTestSuite():
83ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  # This is apparently needed on some systems to make sure that the tests
84ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  # work even if a previous version is already installed.
85ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if 'google' in sys.modules:
86ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    del sys.modules['google']
87ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  GenerateUnittestProtos()
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import unittest
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import google.protobuf.internal.generator_test     as generator_test
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import google.protobuf.internal.descriptor_test    as descriptor_test
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import google.protobuf.internal.reflection_test    as reflection_test
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import google.protobuf.internal.service_reflection_test \
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    as service_reflection_test
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import google.protobuf.internal.text_format_test   as text_format_test
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import google.protobuf.internal.wire_format_test   as wire_format_test
97ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  import google.protobuf.internal.unknown_fields_test as unknown_fields_test
98ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  import google.protobuf.internal.descriptor_database_test \
99ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      as descriptor_database_test
100ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  import google.protobuf.internal.descriptor_pool_test as descriptor_pool_test
101ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  import google.protobuf.internal.message_factory_test as message_factory_test
102ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  import google.protobuf.internal.message_cpp_test as message_cpp_test
103ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  import google.protobuf.internal.reflection_cpp_generated_test \
104ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      as reflection_cpp_generated_test
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  loader = unittest.defaultTestLoader
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  suite = unittest.TestSuite()
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for test in [ generator_test,
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                descriptor_test,
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                reflection_test,
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                service_reflection_test,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                text_format_test,
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                wire_format_test ]:
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suite.addTest(loader.loadTestsFromModule(test))
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return suite
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
118ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
119ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochclass clean(_clean):
120ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  def run(self):
121ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    # Delete generated files in the code tree.
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (dirpath, dirnames, filenames) in os.walk("."):
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for filename in filenames:
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        filepath = os.path.join(dirpath, filename)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \
126ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          filepath.endswith(".so") or filepath.endswith(".o") or \
127ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          filepath.endswith('google/protobuf/compiler/__init__.py'):
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          os.remove(filepath)
129ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    # _clean is an old-style class, so super() doesn't work.
130ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    _clean.run(self)
131ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
132ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochclass build_py(_build_py):
133ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  def run(self):
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Generate necessary .proto file if it doesn't exist.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    generate_proto("../src/google/protobuf/descriptor.proto")
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    generate_proto("../src/google/protobuf/compiler/plugin.proto")
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
138ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    GenerateUnittestProtos()
139ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    # Make sure google.protobuf.compiler is a valid package.
140ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    open('google/protobuf/compiler/__init__.py', 'a').close()
141ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    # _build_py is an old-style class, so super() doesn't work.
142ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    _build_py.run(self)
143ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
144ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochif __name__ == '__main__':
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ext_module_list = []
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # C++ implementation extension
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.getenv("PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION", "python") == "cpp":
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print "Using EXPERIMENTAL C++ Implmenetation."
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ext_module_list.append(Extension(
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "google.protobuf.internal._net_proto2___python",
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        [ "google/protobuf/pyext/python_descriptor.cc",
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "google/protobuf/pyext/python_protobuf.cc",
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "google/protobuf/pyext/python-proto2.cc" ],
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        include_dirs = [ "." ],
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        libraries = [ "protobuf" ]))
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setup(name = 'protobuf',
1593551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        version = '2.5.0-pre',
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        packages = [ 'google' ],
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        namespace_packages = [ 'google' ],
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_suite = 'setup.MakeTestSuite',
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Must list modules explicitly so that we don't install tests.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        py_modules = [
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.api_implementation',
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.containers',
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.cpp_message',
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.decoder',
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.encoder',
1703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          'google.protobuf.internal.enum_type_wrapper',
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.message_listener',
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.python_message',
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.type_checkers',
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.internal.wire_format',
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.descriptor',
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.descriptor_pb2',
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.compiler.plugin_pb2',
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.message',
179ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          'google.protobuf.descriptor_database',
180ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          'google.protobuf.descriptor_pool',
181ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          'google.protobuf.message_factory',
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.reflection',
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.service',
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.service_reflection',
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'google.protobuf.text_format' ],
186ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        cmdclass = { 'clean': clean, 'build_py': build_py },
187ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        install_requires = ['setuptools'],
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ext_modules = ext_module_list,
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        url = 'http://code.google.com/p/protobuf/',
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        maintainer = maintainer_email,
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        maintainer_email = 'protobuf@googlegroups.com',
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        license = 'New BSD License',
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        description = 'Protocol Buffers',
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        long_description =
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "Protocol Buffers are Google's data interchange format.",
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        )
197