setup.py 9.1 KB

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