summaryrefslogtreecommitdiffstats
path: root/setup.py
diff options
context:
space:
mode:
Diffstat (limited to 'setup.py')
-rwxr-xr-xsetup.py611
1 files changed, 611 insertions, 0 deletions
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..ef70f20
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,611 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
+# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+
+import glob
+import os
+import platform
+import sys
+from distutils.command.build import build as _build
+from distutils.command.clean import clean as _clean
+from distutils.command.install_data import install_data as _install_data
+from shutil import rmtree, which
+
+from setuptools import Command, find_packages, setup
+from setuptools.command.test import test as _test
+
+import msgfmt
+from version import get_version
+
+try:
+ from sphinx.setup_command import BuildDoc
+except ImportError:
+
+ class BuildDoc:
+ pass
+
+
+def windows_check():
+ return platform.system() in ('Windows', 'Microsoft')
+
+
+def osx_check():
+ return platform.system() == 'Darwin'
+
+
+desktop_data = 'deluge/ui/data/share/applications/deluge.desktop'
+metainfo_data = 'deluge/ui/data/share/metainfo/deluge.metainfo.xml'
+
+# Variables for setuptools.setup
+_package_data = {}
+_exclude_package_data = {}
+_entry_points = {'console_scripts': [], 'gui_scripts': [], 'deluge.ui': []}
+_data_files = []
+_version = get_version(prefix='deluge-', suffix='.dev0')
+
+
+class PyTest(_test):
+ def initialize_options(self):
+ _test.initialize_options(self)
+ self.pytest_args = []
+
+ def finalize_options(self):
+ _test.finalize_options(self)
+ self.test_args = []
+ self.test_suite = True
+
+ def run_tests(self):
+ import pytest
+
+ errcode = pytest.main(self.test_args)
+ sys.exit(errcode)
+
+
+class CleanDocs(Command):
+ description = 'Clean the documentation build and module rst files'
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ docs_build = 'docs/build'
+ print(f'Deleting {docs_build}')
+ try:
+ rmtree(docs_build)
+ except OSError:
+ pass
+
+ for module in glob.glob('docs/source/modules/deluge*.rst'):
+ os.remove(module)
+
+
+class BuildWebUI(Command):
+ description = 'Minify WebUI files'
+ user_options = []
+
+ JS_DIR = os.path.join('deluge', 'ui', 'web', 'js')
+ JS_SRC_DIRS = ('deluge-all', os.path.join('extjs', 'ext-extensions'))
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ js_basedir = os.path.join(os.path.dirname(__file__), self.JS_DIR)
+
+ try:
+ from minify_web_js import minify_js_dir
+
+ import_error = ''
+ except ImportError as err:
+ import_error = err
+
+ for js_src_dir in self.JS_SRC_DIRS:
+ source_dir = os.path.join(js_basedir, js_src_dir)
+ try:
+ minify_js_dir(source_dir)
+ except NameError:
+ js_file = source_dir + '.js'
+ if os.path.isfile(js_file):
+ print(
+ 'Unable to minify but found existing minified: {}'.format(
+ js_file
+ )
+ )
+ else:
+ # Unable to minify and no existing minified file found so exiting.
+ print('Import error: %s' % import_error)
+ sys.exit(1)
+
+ # Create the gettext.js file for translations.
+ try:
+ from gen_web_gettext import create_gettext_js
+ except ImportError:
+ pass
+ else:
+ deluge_all_path = os.path.join(js_basedir, self.JS_SRC_DIRS[0])
+ print('Creating WebUI translation file: %s/gettext.js' % deluge_all_path)
+ create_gettext_js(deluge_all_path)
+
+
+class CleanWebUI(Command):
+ description = 'Clean the documentation build and rst files'
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ js_basedir = os.path.join(os.path.dirname(__file__), BuildWebUI.JS_DIR)
+
+ # Remove files generated by minify script.
+ for js_src_dir in BuildWebUI.JS_SRC_DIRS:
+ for file_type in ('.js', '-debug.js'):
+ js_file = os.path.join(js_basedir, js_src_dir + file_type)
+ print(f'Deleting {js_file}')
+ try:
+ os.remove(js_file)
+ except OSError:
+ pass
+
+ # Remove generated gettext.js
+ js_file = os.path.join(js_basedir, 'gettext.js')
+ print(f'Deleting {js_file}')
+ try:
+ os.remove(js_file)
+ except OSError:
+ pass
+
+
+class BuildTranslations(Command):
+ description = 'Compile .po files into .mo files & create .desktop file'
+
+ user_options = [
+ ('build-lib', None, 'lib build folder'),
+ ('develop', 'D', 'Compile translations in develop mode (deluge/i18n)'),
+ ]
+ boolean_options = ['develop']
+
+ def initialize_options(self):
+ self.build_lib = None
+ self.develop = False
+
+ def finalize_options(self):
+ self.set_undefined_options('build', ('build_lib', 'build_lib'))
+
+ def run(self):
+ po_dir = os.path.join(os.path.dirname(__file__), 'deluge', 'i18n')
+
+ if self.develop:
+ basedir = po_dir
+ else:
+ basedir = os.path.join(self.build_lib, 'deluge', 'i18n')
+
+ intltool_merge = 'intltool-merge'
+ if not windows_check() and which(intltool_merge):
+ intltool_merge_opts = '--utf8 --quiet'
+ for data_file in (desktop_data, metainfo_data):
+ # creates the translated file from .in file.
+ in_file = data_file + '.in'
+ if 'xml' in data_file:
+ intltool_merge_opts += ' --xml-style'
+ elif 'desktop' in data_file:
+ intltool_merge_opts += ' --desktop-style'
+
+ print('Creating file: %s' % data_file)
+ os.system(
+ 'C_ALL=C '
+ + '%s '
+ * 5
+ % (intltool_merge, intltool_merge_opts, po_dir, in_file, data_file)
+ )
+
+ print('Compiling po files from %s...' % po_dir)
+ for path, names, filenames in os.walk(po_dir):
+ for f in filenames:
+ upto_date = False
+ if f.endswith('.po'):
+ lang = f[: len(f) - 3]
+ src = os.path.join(path, f)
+ dest_path = os.path.join(basedir, lang, 'LC_MESSAGES')
+ dest = os.path.join(dest_path, 'deluge.mo')
+ if not os.path.exists(dest_path):
+ os.makedirs(dest_path)
+ if not os.path.exists(dest):
+ sys.stdout.write('%s, ' % lang)
+ sys.stdout.flush()
+ msgfmt.make(src, dest)
+ else:
+ src_mtime = os.stat(src)[8]
+ dest_mtime = os.stat(dest)[8]
+ if src_mtime > dest_mtime:
+ sys.stdout.write('%s, ' % lang)
+ sys.stdout.flush()
+ msgfmt.make(src, dest)
+ else:
+ upto_date = True
+
+ if upto_date:
+ sys.stdout.write(' po files already up to date. ')
+ sys.stdout.write('\b\b \nFinished compiling translation files. \n')
+
+
+class CleanTranslations(Command):
+ description = 'Cleans translations files.'
+ user_options = [
+ ('all', 'a', 'Remove all build output, not just temporary by-products')
+ ]
+ boolean_options = ['all']
+
+ def initialize_options(self):
+ self.all = None
+
+ def finalize_options(self):
+ self.set_undefined_options('clean', ('all', 'all'))
+
+ def run(self):
+ for path in (desktop_data, metainfo_data):
+ if os.path.isfile(path):
+ print('Deleting %s' % path)
+ os.remove(path)
+
+
+class BuildPlugins(Command):
+ description = 'Build plugins into .eggs'
+
+ user_options = [
+ ('install-dir=', None, 'develop install folder'),
+ ('develop', 'D', 'Compile plugins in develop mode'),
+ ]
+ boolean_options = ['develop']
+
+ def initialize_options(self):
+ self.install_dir = None
+ self.develop = False
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ # Build the plugin eggs
+ plugin_path = 'deluge/plugins/*'
+
+ for path in glob.glob(plugin_path):
+ if os.path.exists(os.path.join(path, 'setup.py')):
+ if self.develop and self.install_dir:
+ os.system(
+ 'cd '
+ + path
+ + '&& '
+ + sys.executable
+ + ' setup.py develop --install-dir=%s' % self.install_dir
+ )
+ elif self.develop:
+ os.system(
+ 'cd ' + path + '&& ' + sys.executable + ' setup.py develop'
+ )
+ else:
+ os.system(
+ 'cd '
+ + path
+ + '&& '
+ + sys.executable
+ + ' setup.py bdist_egg -d ..'
+ )
+
+
+class CleanPlugins(Command):
+ description = 'Cleans the plugin folders'
+ user_options = [
+ ('all', 'a', 'Remove all build output, not just temporary by-products')
+ ]
+ boolean_options = ['all']
+
+ def initialize_options(self):
+ self.all = None
+
+ def finalize_options(self):
+ self.set_undefined_options('clean', ('all', 'all'))
+
+ def run(self):
+ print('Cleaning the plugin\'s folders...')
+
+ plugin_path = 'deluge/plugins/*'
+
+ for path in glob.glob(plugin_path):
+ if os.path.exists(os.path.join(path, 'setup.py')):
+ c = 'cd ' + path + ' && ' + sys.executable + ' setup.py clean'
+ if self.all:
+ c += ' -a'
+ print('Calling \'%s\'' % c)
+ os.system(c)
+
+ # Delete the .eggs
+ if path[-4:] == '.egg':
+ print('Deleting egg file "%s"' % path)
+ os.remove(path)
+
+ # Delete the .egg-link
+ if path[-9:] == '.egg-link':
+ print('Deleting egg link "%s"' % path)
+ os.remove(path)
+
+ egg_info_dir_path = 'deluge/plugins/*/*.egg-info'
+
+ for path in glob.glob(egg_info_dir_path):
+ # Delete the .egg-info's directories
+ if path[-9:] == '.egg-info':
+ print('Deleting %s' % path)
+ for fpath in os.listdir(path):
+ os.remove(os.path.join(path, fpath))
+ os.removedirs(path)
+
+
+class EggInfoPlugins(Command):
+ description = 'Create .egg-info directories for plugins'
+
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ # Build the plugin eggs
+ plugin_path = 'deluge/plugins/*'
+
+ for path in glob.glob(plugin_path):
+ if os.path.exists(os.path.join(path, 'setup.py')):
+ os.system('cd ' + path + '&& ' + sys.executable + ' setup.py egg_info')
+
+
+class Build(_build):
+ sub_commands = [
+ ('build_webui', None),
+ ('build_trans', None),
+ ('build_plugins', None),
+ ] + _build.sub_commands
+
+ def run(self):
+ # Run all sub-commands (at least those that need to be run).
+ _build.run(self)
+ try:
+ from deluge._libtorrent import LT_VERSION
+
+ print(f'Info: Found libtorrent ({LT_VERSION}) installed.')
+ except ImportError as ex:
+ print('Warning: libtorrent (libtorrent-rasterbar) not found: %s' % ex)
+
+
+class InstallData(_install_data):
+ """Custom class to fix `setup install` copying data files to incorrect location. (Bug #1389)"""
+
+ def finalize_options(self):
+ self.install_dir = None
+ self.set_undefined_options(
+ 'install',
+ ('install_data', 'install_dir'),
+ ('root', 'root'),
+ ('force', 'force'),
+ )
+
+ def run(self):
+ _install_data.run(self)
+
+
+class Clean(_clean):
+ sub_commands = _clean.sub_commands + [
+ ('clean_plugins', None),
+ ('clean_trans', None),
+ ('clean_webui', None),
+ ]
+
+ def run(self):
+ # Remove deluge egg-info.
+ root_egg_info_dir_path = 'deluge*.egg-info'
+ for path in glob.glob(root_egg_info_dir_path):
+ print('Deleting %s' % path)
+ for fpath in os.listdir(path):
+ os.remove(os.path.join(path, fpath))
+ os.removedirs(path)
+
+ # Run all sub-commands (at least those that need to be run)
+ for cmd_name in self.get_sub_commands():
+ self.run_command(cmd_name)
+ _clean.run(self)
+
+
+cmdclass = {
+ 'build': Build,
+ 'build_webui': BuildWebUI,
+ 'build_trans': BuildTranslations,
+ 'build_plugins': BuildPlugins,
+ 'build_docs': BuildDoc,
+ 'spellcheck_docs': BuildDoc,
+ 'install_data': InstallData,
+ 'clean_plugins': CleanPlugins,
+ 'clean_trans': CleanTranslations,
+ 'clean_docs': CleanDocs,
+ 'clean_webui': CleanWebUI,
+ 'clean': Clean,
+ 'egg_info_plugins': EggInfoPlugins,
+ 'test': PyTest,
+}
+
+
+if not windows_check() and not osx_check():
+ for icon_path in glob.glob('deluge/ui/data/icons/hicolor/*x*'):
+ size = os.path.basename(icon_path)
+ icons = glob.glob(os.path.join(icon_path, 'apps', 'deluge*.png'))
+ _data_files.append((f'share/icons/hicolor/{size}/apps', icons))
+ _data_files.extend(
+ [
+ (
+ 'share/icons/hicolor/scalable/apps',
+ ['deluge/ui/data/icons/hicolor/scalable/apps/deluge.svg'],
+ ),
+ ('share/pixmaps', ['deluge/ui/data/pixmaps/deluge.png']),
+ (
+ 'share/man/man1',
+ [
+ 'docs/man/deluge.1',
+ 'docs/man/deluged.1',
+ 'docs/man/deluge-gtk.1',
+ 'docs/man/deluge-web.1',
+ 'docs/man/deluge-console.1',
+ ],
+ ),
+ ]
+ )
+ if os.path.isfile(desktop_data):
+ _data_files.append(('share/applications', [desktop_data]))
+ if os.path.isfile(metainfo_data):
+ _data_files.append(('share/metainfo', [metainfo_data]))
+
+
+# Entry Points
+_entry_points['console_scripts'] = [
+ 'deluge-console = deluge.ui.console:start',
+]
+
+# On Windows use gui_scripts to hide cmd popup (no effect on Linux/MacOS)
+_entry_points['gui_scripts'] = [
+ 'deluge = deluge.ui.ui_entry:start_ui',
+ 'deluge-gtk = deluge.ui.gtk3:start',
+ 'deluge-web = deluge.ui.web:start',
+ 'deluged = deluge.core.daemon_entry:start_daemon',
+]
+
+# Provide Windows 'debug' exes for stdin/stdout e.g. logging/errors
+if windows_check():
+ _entry_points['console_scripts'].extend(
+ [
+ 'deluge-debug = deluge.ui.ui_entry:start_ui',
+ 'deluge-web-debug = deluge.ui.web:start',
+ 'deluged-debug = deluge.core.daemon_entry:start_daemon',
+ ]
+ )
+
+_entry_points['deluge.ui'] = [
+ 'console = deluge.ui.console:Console',
+ 'web = deluge.ui.web:Web',
+ 'gtk = deluge.ui.gtk3:Gtk',
+]
+
+
+_package_data['deluge'] = [
+ 'ui/data/pixmaps/*.png',
+ 'ui/data/pixmaps/*.svg',
+ 'ui/data/pixmaps/*.ico',
+ 'ui/data/pixmaps/*.gif',
+ 'ui/data/pixmaps/flags/*.png',
+ 'plugins/*.egg',
+ 'i18n/*/LC_MESSAGES/*.mo',
+]
+_package_data['deluge.ui.web'] = [
+ 'index.html',
+ 'css/*.css',
+ 'icons/*.png',
+ 'images/*.gif',
+ 'images/*.png',
+ 'js/*.js',
+ 'js/extjs/*.js',
+ 'render/*.html',
+ 'themes/css/*.css',
+ 'themes/images/*/*.gif',
+ 'themes/images/*/*.png',
+ 'themes/images/*/*/*.gif',
+ 'themes/images/*/*/*.png',
+]
+_package_data['deluge.ui.gtk3'] = ['glade/*.ui']
+
+setup_requires = ['setuptools', 'wheel']
+install_requires = [
+ 'twisted[tls]>=17.1',
+ # Add pyasn1 for setuptools workaround:
+ # https://github.com/pypa/setuptools/issues/1510
+ 'pyasn1',
+ 'rencode',
+ 'pyopenssl',
+ 'pyxdg',
+ 'mako',
+ 'setuptools',
+ "pywin32; sys_platform == 'win32'",
+ "certifi; sys_platform == 'win32'",
+ 'zope.interface',
+]
+extras_require = {
+ 'all': [
+ 'setproctitle',
+ 'pillow',
+ 'chardet',
+ 'ifaddr',
+ ]
+}
+
+# Main setup
+setup(
+ name='deluge',
+ version=_version,
+ fullname='Deluge BitTorrent Client',
+ description='BitTorrent Client',
+ author='Deluge Team',
+ maintainer='Calum Lind',
+ maintainer_email='calumlind+deluge@gmail.com',
+ keywords='torrent bittorrent p2p fileshare filesharing',
+ long_description=open('README.md').read(),
+ long_description_content_type='text/markdown',
+ url='https://deluge-torrent.org',
+ project_urls={
+ 'GitHub (mirror)': 'https://github.com/deluge-torrent/deluge',
+ 'Sourcecode': 'http://git.deluge-torrent.org/deluge',
+ 'Issues': 'https://dev.deluge-torrent.org/report/1',
+ 'Discussion': 'https://forum.deluge-torrent.org',
+ 'Documentation': 'https://deluge.readthedocs.io',
+ },
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Console',
+ 'Environment :: Web Environment',
+ 'Environment :: X11 Applications :: GTK',
+ 'Framework :: Twisted',
+ 'Intended Audience :: End Users/Desktop',
+ (
+ 'License :: OSI Approved :: '
+ 'GNU General Public License v3 or later (GPLv3+)'
+ ),
+ 'Programming Language :: Python',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: POSIX',
+ 'Topic :: Internet',
+ ],
+ python_requires='>=3.6',
+ license='GPLv3+',
+ cmdclass=cmdclass,
+ setup_requires=setup_requires,
+ install_requires=install_requires,
+ extras_require=extras_require,
+ data_files=_data_files,
+ package_data=_package_data,
+ exclude_package_data=_exclude_package_data,
+ packages=find_packages(exclude=['deluge.plugins.*', 'deluge.tests']),
+ entry_points=_entry_points,
+)