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