setup.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import os
  2. import io
  3. import re
  4. import sys
  5. from collections import defaultdict
  6. from collections import OrderedDict
  7. from datetime import datetime
  8. from glob import glob
  9. try:
  10. import multiprocessing
  11. except ImportError:
  12. multiprocessing = None
  13. from distutils.command.clean import clean
  14. from setuptools.command.build_ext import build_ext
  15. from setuptools import setup, find_packages, Extension
  16. from setuptools.command.sdist import sdist
  17. try:
  18. from Cython.Build import cythonize
  19. except ImportError:
  20. cythonize = None
  21. import setup_lz4
  22. import setup_zstd
  23. import setup_b2
  24. import setup_crypto
  25. import setup_docs
  26. # True: use the shared liblz4 (>= 1.7.0 / r129) from the system, False: use the bundled lz4 code
  27. prefer_system_liblz4 = True
  28. # True: use the shared libzstd (>= 1.3.0) from the system, False: use the bundled zstd code
  29. prefer_system_libzstd = True
  30. # True: use the shared libb2 (>= 0.98.1) from the system, False: use the bundled blake2 code
  31. prefer_system_libb2 = True
  32. cpu_threads = multiprocessing.cpu_count() if multiprocessing else 1
  33. # Are we building on ReadTheDocs?
  34. on_rtd = os.environ.get('READTHEDOCS')
  35. install_requires = [
  36. # we are rather picky about msgpack versions, because a good working msgpack is
  37. # very important for borg, see: https://github.com/borgbackup/borg/issues/3753
  38. 'msgpack >=0.5.6, <=0.6.1',
  39. # Please note:
  40. # using any other version is not supported by borg development and
  41. # any feedback related to issues caused by this will be ignored.
  42. ]
  43. # note for package maintainers: if you package borgbackup for distribution,
  44. # please add llfuse as a *requirement* on all platforms that have a working
  45. # llfuse package. "borg mount" needs llfuse to work.
  46. # if you do not have llfuse, do not require it, most of borgbackup will work.
  47. extras_require = {
  48. # llfuse 1.x should work, llfuse 2.0 will break API
  49. 'fuse': [
  50. 'llfuse >=1.1, <2.0',
  51. 'llfuse >=1.3.4; python_version >="3.7"',
  52. ],
  53. }
  54. compress_source = 'src/borg/compress.pyx'
  55. crypto_ll_source = 'src/borg/crypto/low_level.pyx'
  56. crypto_helpers = 'src/borg/crypto/_crypto_helpers.c'
  57. chunker_source = 'src/borg/chunker.pyx'
  58. hashindex_source = 'src/borg/hashindex.pyx'
  59. item_source = 'src/borg/item.pyx'
  60. checksums_source = 'src/borg/algorithms/checksums.pyx'
  61. platform_posix_source = 'src/borg/platform/posix.pyx'
  62. platform_linux_source = 'src/borg/platform/linux.pyx'
  63. platform_darwin_source = 'src/borg/platform/darwin.pyx'
  64. platform_freebsd_source = 'src/borg/platform/freebsd.pyx'
  65. cython_sources = [
  66. compress_source,
  67. crypto_ll_source,
  68. chunker_source,
  69. hashindex_source,
  70. item_source,
  71. checksums_source,
  72. platform_posix_source,
  73. platform_linux_source,
  74. platform_freebsd_source,
  75. platform_darwin_source,
  76. ]
  77. if cythonize:
  78. Sdist = sdist
  79. else:
  80. class Sdist(sdist):
  81. def __init__(self, *args, **kwargs):
  82. raise Exception('Cython is required to run sdist')
  83. if not on_rtd and not all(os.path.exists(path) for path in [
  84. compress_source, crypto_ll_source, chunker_source, hashindex_source, item_source, checksums_source,
  85. platform_posix_source, platform_linux_source, platform_freebsd_source, platform_darwin_source]):
  86. raise ImportError('The GIT version of Borg needs Cython. Install Cython or use a released version.')
  87. with open('README.rst', 'r') as fd:
  88. long_description = fd.read()
  89. # remove header, but have one \n before first headline
  90. start = long_description.find('What is BorgBackup?')
  91. assert start >= 0
  92. long_description = '\n' + long_description[start:]
  93. # remove badges
  94. long_description = re.compile(r'^\.\. start-badges.*^\.\. end-badges', re.M | re.S).sub('', long_description)
  95. # remove unknown directives
  96. long_description = re.compile(r'^\.\. highlight:: \w+$', re.M).sub('', long_description)
  97. def rm(file):
  98. try:
  99. os.unlink(file)
  100. print('rm', file)
  101. except FileNotFoundError:
  102. pass
  103. class Clean(clean):
  104. def run(self):
  105. super().run()
  106. for source in cython_sources:
  107. genc = source.replace('.pyx', '.c')
  108. rm(genc)
  109. compiled_glob = source.replace('.pyx', '.cpython*')
  110. for compiled in sorted(glob(compiled_glob)):
  111. rm(compiled)
  112. cmdclass = {
  113. 'build_ext': build_ext,
  114. 'build_usage': setup_docs.build_usage,
  115. 'build_man': setup_docs.build_man,
  116. 'sdist': Sdist,
  117. 'clean': Clean,
  118. }
  119. ext_modules = []
  120. if not on_rtd:
  121. def members_appended(*ds):
  122. result = defaultdict(list)
  123. for d in ds:
  124. for k, v in d.items():
  125. assert isinstance(v, list)
  126. result[k].extend(v)
  127. return result
  128. compress_ext_kwargs = members_appended(
  129. dict(sources=[compress_source]),
  130. setup_lz4.lz4_ext_kwargs(prefer_system_liblz4),
  131. setup_zstd.zstd_ext_kwargs(prefer_system_libzstd, multithreaded=False, legacy=False),
  132. )
  133. crypto_ext_kwargs = members_appended(
  134. dict(sources=[crypto_ll_source, crypto_helpers]),
  135. setup_crypto.crypto_ext_kwargs(),
  136. setup_b2.b2_ext_kwargs(prefer_system_libb2),
  137. )
  138. ext_modules += [
  139. Extension('borg.compress', **compress_ext_kwargs),
  140. Extension('borg.crypto.low_level', **crypto_ext_kwargs),
  141. Extension('borg.hashindex', [hashindex_source]),
  142. Extension('borg.item', [item_source]),
  143. Extension('borg.chunker', [chunker_source]),
  144. Extension('borg.algorithms.checksums', [checksums_source]),
  145. ]
  146. posix_ext = Extension('borg.platform.posix', [platform_posix_source])
  147. linux_ext = Extension('borg.platform.linux', [platform_linux_source], libraries=['acl'])
  148. freebsd_ext = Extension('borg.platform.freebsd', [platform_freebsd_source])
  149. darwin_ext = Extension('borg.platform.darwin', [platform_darwin_source])
  150. if not sys.platform.startswith(('win32', )):
  151. ext_modules.append(posix_ext)
  152. if sys.platform == 'linux':
  153. ext_modules.append(linux_ext)
  154. elif sys.platform.startswith('freebsd'):
  155. ext_modules.append(freebsd_ext)
  156. elif sys.platform == 'darwin':
  157. ext_modules.append(darwin_ext)
  158. # sometimes there's no need to cythonize
  159. # this breaks chained commands like 'clean sdist'
  160. cythonizing = len(sys.argv) > 1 and sys.argv[1] not in ('clean', 'egg_info', '--help-commands', '--version') \
  161. and '--help' not in sys.argv[1:]
  162. if cythonize and cythonizing:
  163. cython_opts = dict(
  164. # compile .pyx extensions to .c in parallel
  165. nthreads=cpu_threads + 1,
  166. # default language_level will be '3str' starting from Cython 3.0.0,
  167. # but old cython versions (< 0.29) do not know that, thus we use 3 for now.
  168. compiler_directives={'language_level': 3},
  169. )
  170. cythonize([posix_ext, linux_ext, freebsd_ext, darwin_ext], **cython_opts)
  171. ext_modules = cythonize(ext_modules, **cython_opts)
  172. setup(
  173. name='borgbackup',
  174. use_scm_version={
  175. 'write_to': 'src/borg/_version.py',
  176. },
  177. author='The Borg Collective (see AUTHORS file)',
  178. author_email='borgbackup@python.org',
  179. url='https://borgbackup.readthedocs.io/',
  180. description='Deduplicated, encrypted, authenticated and compressed backups',
  181. long_description=long_description,
  182. license='BSD',
  183. platforms=['Linux', 'MacOS X', 'FreeBSD', 'OpenBSD', 'NetBSD', ],
  184. classifiers=[
  185. 'Development Status :: 3 - Alpha',
  186. 'Environment :: Console',
  187. 'Intended Audience :: System Administrators',
  188. 'License :: OSI Approved :: BSD License',
  189. 'Operating System :: POSIX :: BSD :: FreeBSD',
  190. 'Operating System :: POSIX :: BSD :: OpenBSD',
  191. 'Operating System :: POSIX :: BSD :: NetBSD',
  192. 'Operating System :: MacOS :: MacOS X',
  193. 'Operating System :: POSIX :: Linux',
  194. 'Programming Language :: Python',
  195. 'Programming Language :: Python :: 3',
  196. 'Programming Language :: Python :: 3.5',
  197. 'Programming Language :: Python :: 3.6',
  198. 'Programming Language :: Python :: 3.7',
  199. 'Topic :: Security :: Cryptography',
  200. 'Topic :: System :: Archiving :: Backup',
  201. ],
  202. packages=find_packages('src'),
  203. package_dir={'': 'src'},
  204. zip_safe=False,
  205. entry_points={
  206. 'console_scripts': [
  207. 'borg = borg.archiver:main',
  208. 'borgfs = borg.archiver:main',
  209. ]
  210. },
  211. # See also the MANIFEST.in file.
  212. # We want to install all the files in the package directories...
  213. include_package_data=True,
  214. # ...except the source files which have been compiled (C extensions):
  215. exclude_package_data={
  216. '': ['*.c', '*.h', '*.pyx', ],
  217. },
  218. cmdclass=cmdclass,
  219. ext_modules=ext_modules,
  220. setup_requires=['setuptools_scm>=1.7'],
  221. install_requires=install_requires,
  222. extras_require=extras_require,
  223. python_requires='>=3.5',
  224. )