setup.py 8.9 KB

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