|
@@ -1,8 +1,15 @@
|
|
|
# -*- encoding: utf-8 *-*
|
|
|
import os
|
|
|
+import re
|
|
|
import sys
|
|
|
from glob import glob
|
|
|
|
|
|
+from distutils.command.build import build
|
|
|
+from distutils.core import Command
|
|
|
+from distutils.errors import DistutilsOptionError
|
|
|
+from distutils import log
|
|
|
+from setuptools.command.build_py import build_py
|
|
|
+
|
|
|
min_python = (3, 2)
|
|
|
my_python = sys.version_info
|
|
|
|
|
@@ -10,6 +17,9 @@ if my_python < min_python:
|
|
|
print("Borg requires Python %d.%d or later" % min_python)
|
|
|
sys.exit(1)
|
|
|
|
|
|
+# Are we building on ReadTheDocs?
|
|
|
+on_rtd = os.environ.get('READTHEDOCS')
|
|
|
+
|
|
|
# msgpack pure python data corruption was fixed in 0.4.6.
|
|
|
# Also, we might use some rather recent API features.
|
|
|
install_requires=['msgpack-python>=0.4.6', ]
|
|
@@ -62,7 +72,7 @@ except ImportError:
|
|
|
platform_freebsd_source = platform_freebsd_source.replace('.pyx', '.c')
|
|
|
platform_darwin_source = platform_darwin_source.replace('.pyx', '.c')
|
|
|
from distutils.command.build_ext import build_ext
|
|
|
- if not all(os.path.exists(path) for path in [
|
|
|
+ if not on_rtd and not all(os.path.exists(path) for path in [
|
|
|
compress_source, crypto_source, chunker_source, hashindex_source,
|
|
|
platform_linux_source, platform_freebsd_source]):
|
|
|
raise ImportError('The GIT version of Borg needs Cython. Install Cython or use a released version.')
|
|
@@ -103,29 +113,140 @@ possible_lz4_prefixes = ['/usr', '/usr/local', '/usr/local/opt/lz4', '/usr/local
|
|
|
if os.environ.get('BORG_LZ4_PREFIX'):
|
|
|
possible_openssl_prefixes.insert(0, os.environ.get('BORG_LZ4_PREFIX'))
|
|
|
lz4_prefix = detect_lz4(possible_lz4_prefixes)
|
|
|
-if not lz4_prefix:
|
|
|
+if lz4_prefix:
|
|
|
+ include_dirs.append(os.path.join(lz4_prefix, 'include'))
|
|
|
+ library_dirs.append(os.path.join(lz4_prefix, 'lib'))
|
|
|
+elif not on_rtd:
|
|
|
raise Exception('Unable to find LZ4 headers. (Looked here: {})'.format(', '.join(possible_lz4_prefixes)))
|
|
|
-include_dirs.append(os.path.join(lz4_prefix, 'include'))
|
|
|
-library_dirs.append(os.path.join(lz4_prefix, 'lib'))
|
|
|
|
|
|
|
|
|
with open('README.rst', 'r') as fd:
|
|
|
long_description = fd.read()
|
|
|
|
|
|
-cmdclass = {'build_ext': build_ext, 'sdist': Sdist}
|
|
|
+class build_usage(Command):
|
|
|
+ description = "generate usage for each command"
|
|
|
+
|
|
|
+ user_options = [
|
|
|
+ ('output=', 'O', 'output directory'),
|
|
|
+ ]
|
|
|
+ def initialize_options(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def finalize_options(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ print('generating usage docs')
|
|
|
+ # allows us to build docs without the C modules fully loaded during help generation
|
|
|
+ if 'BORG_CYTHON_DISABLE' not in os.environ:
|
|
|
+ os.environ['BORG_CYTHON_DISABLE'] = self.__class__.__name__
|
|
|
+ from borg.archiver import Archiver
|
|
|
+ parser = Archiver().build_parser(prog='borg')
|
|
|
+ # return to regular Cython configuration, if we changed it
|
|
|
+ if os.environ.get('BORG_CYTHON_DISABLE') == self.__class__.__name__:
|
|
|
+ del os.environ['BORG_CYTHON_DISABLE']
|
|
|
+ choices = {}
|
|
|
+ for action in parser._actions:
|
|
|
+ if action.choices is not None:
|
|
|
+ choices.update(action.choices)
|
|
|
+ print('found commands: %s' % list(choices.keys()))
|
|
|
+ if not os.path.exists('docs/usage'):
|
|
|
+ os.mkdir('docs/usage')
|
|
|
+ for command, parser in choices.items():
|
|
|
+ if command is 'help':
|
|
|
+ continue
|
|
|
+ with open('docs/usage/%s.rst.inc' % command, 'w') as doc:
|
|
|
+ print('generating help for %s' % command)
|
|
|
+ params = {"command": command,
|
|
|
+ "underline": '-' * len('borg ' + command)}
|
|
|
+ doc.write(".. _borg_{command}:\n\n".format(**params))
|
|
|
+ doc.write("borg {command}\n{underline}\n::\n\n".format(**params))
|
|
|
+ epilog = parser.epilog
|
|
|
+ parser.epilog = None
|
|
|
+ doc.write(re.sub("^", " ", parser.format_help(), flags=re.M))
|
|
|
+ doc.write("\nDescription\n~~~~~~~~~~~\n")
|
|
|
+ doc.write(epilog)
|
|
|
+
|
|
|
+
|
|
|
+class build_api(Command):
|
|
|
+ description = "generate a basic api.rst file based on the modules available"
|
|
|
+
|
|
|
+ user_options = [
|
|
|
+ ('output=', 'O', 'output directory'),
|
|
|
+ ]
|
|
|
+ def initialize_options(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def finalize_options(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def run(self):
|
|
|
+ print("auto-generating API documentation")
|
|
|
+ with open("docs/api.rst", "w") as doc:
|
|
|
+ doc.write("""
|
|
|
+Borg Backup API documentation"
|
|
|
+=============================
|
|
|
+""")
|
|
|
+ for mod in glob('borg/*.py') + glob('borg/*.pyx'):
|
|
|
+ print("examining module %s" % mod)
|
|
|
+ if "/_" not in mod:
|
|
|
+ doc.write("""
|
|
|
+.. automodule:: %s
|
|
|
+ :members:
|
|
|
+ :undoc-members:
|
|
|
+""" % mod)
|
|
|
+
|
|
|
+# (function, predicate), see http://docs.python.org/2/distutils/apiref.html#distutils.cmd.Command.sub_commands
|
|
|
+# seems like this doesn't work on RTD, see below for build_py hack.
|
|
|
+build.sub_commands.append(('build_api', None))
|
|
|
+build.sub_commands.append(('build_usage', None))
|
|
|
+
|
|
|
+
|
|
|
+class build_py_custom(build_py):
|
|
|
+ """override build_py to also build our stuff
|
|
|
+
|
|
|
+ it is unclear why this is necessary, but in some environments
|
|
|
+ (Readthedocs.org, specifically), the above
|
|
|
+ ``build.sub_commands.append()`` doesn't seem to have an effect:
|
|
|
+ our custom build commands seem to be ignored when running
|
|
|
+ ``setup.py install``.
|
|
|
+
|
|
|
+ This class overrides the ``build_py`` target by forcing it to run
|
|
|
+ our custom steps as well.
|
|
|
+
|
|
|
+ See also the `bug report on RTD
|
|
|
+ <https://github.com/rtfd/readthedocs.org/issues/1740>`_.
|
|
|
+ """
|
|
|
+ def run(self):
|
|
|
+ super().run()
|
|
|
+ self.announce('calling custom build steps', level=log.INFO)
|
|
|
+ self.run_command('build_ext')
|
|
|
+ self.run_command('build_api')
|
|
|
+ self.run_command('build_usage')
|
|
|
+
|
|
|
+
|
|
|
+cmdclass = {
|
|
|
+ 'build_ext': build_ext,
|
|
|
+ 'build_api': build_api,
|
|
|
+ 'build_usage': build_usage,
|
|
|
+ 'build_py': build_py_custom,
|
|
|
+ 'sdist': Sdist
|
|
|
+}
|
|
|
|
|
|
-ext_modules = [
|
|
|
+ext_modules = []
|
|
|
+if not on_rtd:
|
|
|
+ ext_modules += [
|
|
|
Extension('borg.compress', [compress_source], libraries=['lz4'], include_dirs=include_dirs, library_dirs=library_dirs),
|
|
|
Extension('borg.crypto', [crypto_source], libraries=['crypto'], include_dirs=include_dirs, library_dirs=library_dirs),
|
|
|
Extension('borg.chunker', [chunker_source]),
|
|
|
Extension('borg.hashindex', [hashindex_source])
|
|
|
]
|
|
|
-if sys.platform.startswith('linux'):
|
|
|
- ext_modules.append(Extension('borg.platform_linux', [platform_linux_source], libraries=['acl']))
|
|
|
-elif sys.platform.startswith('freebsd'):
|
|
|
- ext_modules.append(Extension('borg.platform_freebsd', [platform_freebsd_source]))
|
|
|
-elif sys.platform == 'darwin':
|
|
|
- ext_modules.append(Extension('borg.platform_darwin', [platform_darwin_source]))
|
|
|
+ if sys.platform.startswith('linux'):
|
|
|
+ ext_modules.append(Extension('borg.platform_linux', [platform_linux_source], libraries=['acl']))
|
|
|
+ elif sys.platform.startswith('freebsd'):
|
|
|
+ ext_modules.append(Extension('borg.platform_freebsd', [platform_freebsd_source]))
|
|
|
+ elif sys.platform == 'darwin':
|
|
|
+ ext_modules.append(Extension('borg.platform_darwin', [platform_darwin_source]))
|
|
|
|
|
|
setup(
|
|
|
name='borgbackup',
|