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