1# Copyright 2015 The Brotli Authors. All rights reserved. 2# 3# Distributed under MIT license. 4# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 6import os 7import platform 8import re 9import unittest 10 11try: 12 from setuptools import Extension 13 from setuptools import setup 14except: 15 from distutils.core import Extension 16 from distutils.core import setup 17from distutils.command.build_ext import build_ext 18 19 20CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) 21 22 23def get_version(): 24 """ Return BROTLI_VERSION string as defined in 'common/version.h' file. """ 25 version_file_path = os.path.join(CURR_DIR, 'common', 'version.h') 26 version = 0 27 with open(version_file_path, 'r') as f: 28 for line in f: 29 m = re.match(r'#define\sBROTLI_VERSION\s+0x([0-9a-fA-F]+)', line) 30 if m: 31 version = int(m.group(1), 16) 32 if version == 0: 33 return '' 34 # Semantic version is calculated as (MAJOR << 24) | (MINOR << 12) | PATCH. 35 major = version >> 24 36 minor = (version >> 12) & 0xFFF 37 patch = version & 0xFFF 38 return '{0}.{1}.{2}'.format(major, minor, patch) 39 40 41def get_test_suite(): 42 test_loader = unittest.TestLoader() 43 test_suite = test_loader.discover('python', pattern='*_test.py') 44 return test_suite 45 46 47class BuildExt(build_ext): 48 49 def get_source_files(self): 50 filenames = build_ext.get_source_files(self) 51 for ext in self.extensions: 52 filenames.extend(ext.depends) 53 return filenames 54 55 def build_extension(self, ext): 56 c_sources = [] 57 cxx_sources = [] 58 for source in ext.sources: 59 if source.endswith('.c'): 60 c_sources.append(source) 61 else: 62 cxx_sources.append(source) 63 extra_args = ext.extra_compile_args or [] 64 65 objects = [] 66 for lang, sources in (('c', c_sources), ('c++', cxx_sources)): 67 if lang == 'c++': 68 if self.compiler.compiler_type == 'msvc': 69 extra_args.append('/EHsc') 70 71 macros = ext.define_macros[:] 72 if platform.system() == 'Darwin': 73 macros.append(('OS_MACOSX', '1')) 74 elif self.compiler.compiler_type == 'mingw32': 75 # On Windows Python 2.7, pyconfig.h defines "hypot" as "_hypot", 76 # This clashes with GCC's cmath, and causes compilation errors when 77 # building under MinGW: http://bugs.python.org/issue11566 78 macros.append(('_hypot', 'hypot')) 79 for undef in ext.undef_macros: 80 macros.append((undef,)) 81 82 objs = self.compiler.compile( 83 sources, 84 output_dir=self.build_temp, 85 macros=macros, 86 include_dirs=ext.include_dirs, 87 debug=self.debug, 88 extra_postargs=extra_args, 89 depends=ext.depends) 90 objects.extend(objs) 91 92 self._built_objects = objects[:] 93 if ext.extra_objects: 94 objects.extend(ext.extra_objects) 95 extra_args = ext.extra_link_args or [] 96 # when using GCC on Windows, we statically link libgcc and libstdc++, 97 # so that we don't need to package extra DLLs 98 if self.compiler.compiler_type == 'mingw32': 99 extra_args.extend(['-static-libgcc', '-static-libstdc++']) 100 101 ext_path = self.get_ext_fullpath(ext.name) 102 # Detect target language, if not provided 103 language = ext.language or self.compiler.detect_language(sources) 104 105 self.compiler.link_shared_object( 106 objects, 107 ext_path, 108 libraries=self.get_libraries(ext), 109 library_dirs=ext.library_dirs, 110 runtime_library_dirs=ext.runtime_library_dirs, 111 extra_postargs=extra_args, 112 export_symbols=self.get_export_symbols(ext), 113 debug=self.debug, 114 build_temp=self.build_temp, 115 target_lang=language) 116 117 118NAME = 'Brotli' 119 120VERSION = get_version() 121 122URL = 'https://github.com/google/brotli' 123 124DESCRIPTION = 'Python bindings for the Brotli compression library' 125 126AUTHOR = 'The Brotli Authors' 127 128LICENSE = 'Apache 2.0' 129 130PLATFORMS = ['Posix', 'MacOS X', 'Windows'] 131 132CLASSIFIERS = [ 133 'Development Status :: 4 - Beta', 134 'Environment :: Console', 135 'Intended Audience :: Developers', 136 'License :: OSI Approved :: Apache Software License', 137 'Operating System :: MacOS :: MacOS X', 138 'Operating System :: Microsoft :: Windows', 139 'Operating System :: POSIX :: Linux', 140 'Programming Language :: C', 141 'Programming Language :: C++', 142 'Programming Language :: Python', 143 'Programming Language :: Python :: 2', 144 'Programming Language :: Python :: 2.7', 145 'Programming Language :: Python :: 3', 146 'Programming Language :: Python :: 3.3', 147 'Programming Language :: Python :: 3.4', 148 'Programming Language :: Python :: 3.5', 149 'Programming Language :: Unix Shell', 150 'Topic :: Software Development :: Libraries', 151 'Topic :: Software Development :: Libraries :: Python Modules', 152 'Topic :: System :: Archiving', 153 'Topic :: System :: Archiving :: Compression', 154 'Topic :: Text Processing :: Fonts', 155 'Topic :: Utilities', 156] 157 158PACKAGE_DIR = {'': 'python'} 159 160PY_MODULES = ['brotli'] 161 162EXT_MODULES = [ 163 Extension( 164 '_brotli', 165 sources=[ 166 'python/_brotli.cc', 167 'common/dictionary.c', 168 'dec/bit_reader.c', 169 'dec/decode.c', 170 'dec/huffman.c', 171 'dec/state.c', 172 'enc/backward_references.c', 173 'enc/backward_references_hq.c', 174 'enc/bit_cost.c', 175 'enc/block_splitter.c', 176 'enc/brotli_bit_stream.c', 177 'enc/cluster.c', 178 'enc/compress_fragment.c', 179 'enc/compress_fragment_two_pass.c', 180 'enc/dictionary_hash.c', 181 'enc/encode.c', 182 'enc/entropy_encode.c', 183 'enc/histogram.c', 184 'enc/literal_cost.c', 185 'enc/memory.c', 186 'enc/metablock.c', 187 'enc/static_dict.c', 188 'enc/utf8_util.c', 189 ], 190 depends=[ 191 'common/constants.h', 192 'common/dictionary.h', 193 'common/port.h', 194 'common/version.h', 195 'dec/bit_reader.h', 196 'dec/context.h', 197 'dec/huffman.h', 198 'dec/port.h', 199 'dec/prefix.h', 200 'dec/state.h', 201 'dec/streams.h', 202 'dec/transform.h', 203 'enc/backward_references.h', 204 'enc/backward_references_hq.h', 205 'enc/backward_references_inc.h', 206 'enc/bit_cost.h', 207 'enc/bit_cost_inc.h', 208 'enc/block_splitter.h', 209 'enc/block_splitter_inc.h', 210 'enc/brotli_bit_stream.h', 211 'enc/cluster.h', 212 'enc/cluster_inc.h', 213 'enc/command.h', 214 'enc/compress_fragment.h', 215 'enc/compress_fragment_two_pass.h' 216 'enc/context.h', 217 'enc/dictionary_hash.h', 218 'enc/entropy_encode.h', 219 'enc/entropy_encode_static.h', 220 'enc/fast_log.h', 221 'enc/find_match_length.h', 222 'enc/hash.h', 223 'enc/hash_to_binary_tree_inc.h', 224 'enc/hash_longest_match64_inc.h', 225 'enc/hash_longest_match_inc.h', 226 'enc/hash_longest_match_quickly_inc.h', 227 'enc/histogram.h', 228 'enc/histogram_inc.h', 229 'enc/literal_cost.h', 230 'enc/memory.h', 231 'enc/metablock.h', 232 'enc/metablock_inc.h', 233 'enc/port.h', 234 'enc/prefix.h', 235 'enc/ringbuffer.h', 236 'enc/static_dict.h', 237 'enc/static_dict_lut.h', 238 'enc/utf8_util.h', 239 'enc/write_bits.h', 240 ], 241 include_dirs=[ 242 'include', 243 ], 244 language='c++'), 245] 246 247TEST_SUITE = 'setup.get_test_suite' 248 249CMD_CLASS = { 250 'build_ext': BuildExt, 251} 252 253setup( 254 name=NAME, 255 description=DESCRIPTION, 256 version=VERSION, 257 url=URL, 258 author=AUTHOR, 259 license=LICENSE, 260 platforms=PLATFORMS, 261 classifiers=CLASSIFIERS, 262 package_dir=PACKAGE_DIR, 263 py_modules=PY_MODULES, 264 ext_modules=EXT_MODULES, 265 test_suite=TEST_SUITE, 266 cmdclass=CMD_CLASS) 267