diff options
Diffstat (limited to 'setup.py')
-rwxr-xr-x | setup.py | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..5cae466 --- /dev/null +++ b/setup.py @@ -0,0 +1,605 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# 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. +# + +from __future__ import print_function + +import glob +import os +import platform +import sys +from distutils import cmd +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 distutils.spawn import find_executable +from shutil import rmtree + +from setuptools import 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(object): + 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' +appdata_data = 'deluge/ui/data/share/appdata/deluge.appdata.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(cmd.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('Deleting {}'.format(docs_build)) + try: + rmtree(docs_build) + except OSError: + pass + + for module in glob.glob('docs/source/modules/deluge*.rst'): + os.remove(module) + + +class BuildWebUI(cmd.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(cmd.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('Deleting {}'.format(js_file)) + try: + os.remove(js_file) + except OSError: + pass + + # Remove generated gettext.js + js_file = os.path.join(js_basedir, 'gettext.js') + print('Deleting {}'.format(js_file)) + try: + os.remove(js_file) + except OSError: + pass + + +class BuildTranslations(cmd.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 find_executable(intltool_merge): + intltool_merge_opts = '--utf8 --quiet' + for data_file in (desktop_data, appdata_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 upto date. ') + sys.stdout.write('\b\b \nFinished compiling translation files. \n') + + +class CleanTranslations(cmd.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, appdata_data): + if os.path.isfile(path): + print('Deleting %s' % path) + os.remove(path) + + +class BuildPlugins(cmd.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(cmd.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(cmd.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('Info: Found libtorrent ({}) installed.'.format(LT_VERSION)) + 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, + '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(('share/icons/hicolor/{}/apps'.format(size), 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(appdata_data): + _data_files.append(('share/appdata', [appdata_data])) + +_entry_points['console_scripts'] = [ + 'deluge-console = deluge.ui.console:start', + 'deluge-web = deluge.ui.web:start', + 'deluged = deluge.core.daemon_entry:start_daemon', +] +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['gui_scripts'] = [ + 'deluge = deluge.ui.ui_entry:start_ui', + 'deluge-gtk = deluge.ui.gtk3:start', +] +_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', + 'pillow', + 'mako', + 'chardet', + 'six', + 'setproctitle', + "pywin32; sys_platform == 'win32'", + "py2-ipaddress; sys_platform == 'win32' and python_version == '2'", + "certifi; sys_platform == 'win32'", + 'zope.interface', +] +tests_require = ['pytest', 'pytest-twisted'] + +# 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='>=2.7', + license='GPLv3+', + cmdclass=cmdclass, + setup_requires=setup_requires, + install_requires=install_requires, + tests_require=tests_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, +) |