Home | History | Annotate | Download | only in python
      1 #! /usr/bin/env python
      2 #
      3 # See README for usage instructions.
      4 import glob
      5 import os
      6 import subprocess
      7 import sys
      8 
      9 # We must use setuptools, not distutils, because we need to use the
     10 # namespace_packages option for the "google" package.
     11 from setuptools import setup, Extension, find_packages
     12 
     13 from distutils.command.clean import clean as _clean
     14 
     15 if sys.version_info[0] == 3:
     16   # Python 3
     17   from distutils.command.build_py import build_py_2to3 as _build_py
     18 else:
     19   # Python 2
     20   from distutils.command.build_py import build_py as _build_py
     21 from distutils.spawn import find_executable
     22 
     23 # Find the Protocol Compiler.
     24 if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']):
     25   protoc = os.environ['PROTOC']
     26 elif os.path.exists("../src/protoc"):
     27   protoc = "../src/protoc"
     28 elif os.path.exists("../src/protoc.exe"):
     29   protoc = "../src/protoc.exe"
     30 elif os.path.exists("../vsprojects/Debug/protoc.exe"):
     31   protoc = "../vsprojects/Debug/protoc.exe"
     32 elif os.path.exists("../vsprojects/Release/protoc.exe"):
     33   protoc = "../vsprojects/Release/protoc.exe"
     34 else:
     35   protoc = find_executable("protoc")
     36 
     37 
     38 def GetVersion():
     39   """Gets the version from google/protobuf/__init__.py
     40 
     41   Do not import google.protobuf.__init__ directly, because an installed
     42   protobuf library may be loaded instead."""
     43 
     44   with open(os.path.join('google', 'protobuf', '__init__.py')) as version_file:
     45     exec(version_file.read(), globals())
     46     return __version__
     47 
     48 
     49 def generate_proto(source, require = True):
     50   """Invokes the Protocol Compiler to generate a _pb2.py from the given
     51   .proto file.  Does nothing if the output already exists and is newer than
     52   the input."""
     53 
     54   if not require and not os.path.exists(source):
     55     return
     56 
     57   output = source.replace(".proto", "_pb2.py").replace("../src/", "")
     58 
     59   if (not os.path.exists(output) or
     60       (os.path.exists(source) and
     61        os.path.getmtime(source) > os.path.getmtime(output))):
     62     print("Generating %s..." % output)
     63 
     64     if not os.path.exists(source):
     65       sys.stderr.write("Can't find required file: %s\n" % source)
     66       sys.exit(-1)
     67 
     68     if protoc is None:
     69       sys.stderr.write(
     70           "protoc is not installed nor found in ../src.  Please compile it "
     71           "or install the binary package.\n")
     72       sys.exit(-1)
     73 
     74     protoc_command = [ protoc, "-I../src", "-I.", "--python_out=.", source ]
     75     if subprocess.call(protoc_command) != 0:
     76       sys.exit(-1)
     77 
     78 def GenerateUnittestProtos():
     79   generate_proto("../src/google/protobuf/map_unittest.proto", False)
     80   generate_proto("../src/google/protobuf/unittest_arena.proto", False)
     81   generate_proto("../src/google/protobuf/unittest_no_arena.proto", False)
     82   generate_proto("../src/google/protobuf/unittest_no_arena_import.proto", False)
     83   generate_proto("../src/google/protobuf/unittest.proto", False)
     84   generate_proto("../src/google/protobuf/unittest_custom_options.proto", False)
     85   generate_proto("../src/google/protobuf/unittest_import.proto", False)
     86   generate_proto("../src/google/protobuf/unittest_import_public.proto", False)
     87   generate_proto("../src/google/protobuf/unittest_mset.proto", False)
     88   generate_proto("../src/google/protobuf/unittest_mset_wire_format.proto", False)
     89   generate_proto("../src/google/protobuf/unittest_no_generic_services.proto", False)
     90   generate_proto("../src/google/protobuf/unittest_proto3_arena.proto", False)
     91   generate_proto("../src/google/protobuf/util/json_format_proto3.proto", False)
     92   generate_proto("google/protobuf/internal/any_test.proto", False)
     93   generate_proto("google/protobuf/internal/descriptor_pool_test1.proto", False)
     94   generate_proto("google/protobuf/internal/descriptor_pool_test2.proto", False)
     95   generate_proto("google/protobuf/internal/factory_test1.proto", False)
     96   generate_proto("google/protobuf/internal/factory_test2.proto", False)
     97   generate_proto("google/protobuf/internal/import_test_package/inner.proto", False)
     98   generate_proto("google/protobuf/internal/import_test_package/outer.proto", False)
     99   generate_proto("google/protobuf/internal/missing_enum_values.proto", False)
    100   generate_proto("google/protobuf/internal/message_set_extensions.proto", False)
    101   generate_proto("google/protobuf/internal/more_extensions.proto", False)
    102   generate_proto("google/protobuf/internal/more_extensions_dynamic.proto", False)
    103   generate_proto("google/protobuf/internal/more_messages.proto", False)
    104   generate_proto("google/protobuf/internal/packed_field_test.proto", False)
    105   generate_proto("google/protobuf/internal/test_bad_identifiers.proto", False)
    106   generate_proto("google/protobuf/pyext/python.proto", False)
    107 
    108 
    109 class clean(_clean):
    110   def run(self):
    111     # Delete generated files in the code tree.
    112     for (dirpath, dirnames, filenames) in os.walk("."):
    113       for filename in filenames:
    114         filepath = os.path.join(dirpath, filename)
    115         if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \
    116           filepath.endswith(".so") or filepath.endswith(".o") or \
    117           filepath.endswith('google/protobuf/compiler/__init__.py') or \
    118           filepath.endswith('google/protobuf/util/__init__.py'):
    119           os.remove(filepath)
    120     # _clean is an old-style class, so super() doesn't work.
    121     _clean.run(self)
    122 
    123 class build_py(_build_py):
    124   def run(self):
    125     # Generate necessary .proto file if it doesn't exist.
    126     generate_proto("../src/google/protobuf/descriptor.proto")
    127     generate_proto("../src/google/protobuf/compiler/plugin.proto")
    128     generate_proto("../src/google/protobuf/any.proto")
    129     generate_proto("../src/google/protobuf/api.proto")
    130     generate_proto("../src/google/protobuf/duration.proto")
    131     generate_proto("../src/google/protobuf/empty.proto")
    132     generate_proto("../src/google/protobuf/field_mask.proto")
    133     generate_proto("../src/google/protobuf/source_context.proto")
    134     generate_proto("../src/google/protobuf/struct.proto")
    135     generate_proto("../src/google/protobuf/timestamp.proto")
    136     generate_proto("../src/google/protobuf/type.proto")
    137     generate_proto("../src/google/protobuf/wrappers.proto")
    138     GenerateUnittestProtos()
    139 
    140     # Make sure google.protobuf/** are valid packages.
    141     for path in ['', 'internal/', 'compiler/', 'pyext/', 'util/']:
    142       try:
    143         open('google/protobuf/%s__init__.py' % path, 'a').close()
    144       except EnvironmentError:
    145         pass
    146     # _build_py is an old-style class, so super() doesn't work.
    147     _build_py.run(self)
    148 
    149 class test_conformance(_build_py):
    150   target = 'test_python'
    151   def run(self):
    152     if sys.version_info >= (2, 7):
    153       # Python 2.6 dodges these extra failures.
    154       os.environ["CONFORMANCE_PYTHON_EXTRA_FAILURES"] = (
    155           "--failure_list failure_list_python-post26.txt")
    156     cmd = 'cd ../conformance && make %s' % (test_conformance.target)
    157     status = subprocess.check_call(cmd, shell=True)
    158 
    159 
    160 def get_option_from_sys_argv(option_str):
    161   if option_str in sys.argv:
    162     sys.argv.remove(option_str)
    163     return True
    164   return False
    165 
    166 
    167 if __name__ == '__main__':
    168   ext_module_list = []
    169   warnings_as_errors = '--warnings_as_errors'
    170   if get_option_from_sys_argv('--cpp_implementation'):
    171     # Link libprotobuf.a and libprotobuf-lite.a statically with the
    172     # extension. Note that those libraries have to be compiled with
    173     # -fPIC for this to work.
    174     compile_static_ext = get_option_from_sys_argv('--compile_static_extension')
    175     extra_compile_args = ['-Wno-write-strings',
    176                           '-Wno-invalid-offsetof',
    177                           '-Wno-sign-compare']
    178     libraries = ['protobuf']
    179     extra_objects = None
    180     if compile_static_ext:
    181       libraries = None
    182       extra_objects = ['../src/.libs/libprotobuf.a',
    183                        '../src/.libs/libprotobuf-lite.a']
    184     test_conformance.target = 'test_python_cpp'
    185 
    186     if "clang" in os.popen('$CC --version 2> /dev/null').read():
    187       extra_compile_args.append('-Wno-shorten-64-to-32')
    188 
    189     if warnings_as_errors in sys.argv:
    190       extra_compile_args.append('-Werror')
    191       sys.argv.remove(warnings_as_errors)
    192 
    193     # C++ implementation extension
    194     ext_module_list.extend([
    195         Extension(
    196             "google.protobuf.pyext._message",
    197             glob.glob('google/protobuf/pyext/*.cc'),
    198             include_dirs=[".", "../src"],
    199             libraries=libraries,
    200             extra_objects=extra_objects,
    201             library_dirs=['../src/.libs'],
    202             extra_compile_args=extra_compile_args,
    203         ),
    204         Extension(
    205             "google.protobuf.internal._api_implementation",
    206             glob.glob('google/protobuf/internal/api_implementation.cc'),
    207             extra_compile_args=['-DPYTHON_PROTO2_CPP_IMPL_V2'],
    208         ),
    209     ])
    210     os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp'
    211 
    212   # Keep this list of dependencies in sync with tox.ini.
    213   install_requires = ['six>=1.9', 'setuptools']
    214   if sys.version_info <= (2,7):
    215     install_requires.append('ordereddict')
    216     install_requires.append('unittest2')
    217 
    218   setup(
    219       name='protobuf',
    220       version=GetVersion(),
    221       description='Protocol Buffers',
    222       long_description="Protocol Buffers are Google's data interchange format",
    223       url='https://developers.google.com/protocol-buffers/',
    224       maintainer='protobuf (at] googlegroups.com',
    225       maintainer_email='protobuf (at] googlegroups.com',
    226       license='New BSD License',
    227       classifiers=[
    228         "Programming Language :: Python",
    229         "Programming Language :: Python :: 2",
    230         "Programming Language :: Python :: 2.6",
    231         "Programming Language :: Python :: 2.7",
    232         "Programming Language :: Python :: 3",
    233         "Programming Language :: Python :: 3.3",
    234         "Programming Language :: Python :: 3.4",
    235         ],
    236       namespace_packages=['google'],
    237       packages=find_packages(
    238           exclude=[
    239               'import_test_package',
    240           ],
    241       ),
    242       test_suite='google.protobuf.internal',
    243       cmdclass={
    244           'clean': clean,
    245           'build_py': build_py,
    246           'test_conformance': test_conformance,
    247       },
    248       install_requires=install_requires,
    249       ext_modules=ext_module_list,
    250   )
    251