summaryrefslogtreecommitdiff
path: root/setup.py
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
committerPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2017-08-18 14:48:52 +0200
commitf7bdc2acff3c13a6d632c28c4569690ab106eed7 (patch)
tree9d67cdb7152ee4e711379e03fe0546c7c3b97303 /setup.py
Import Upstream version 0.5.0+dfsg
Diffstat (limited to 'setup.py')
-rw-r--r--setup.py745
1 files changed, 745 insertions, 0 deletions
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..852c48d
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,745 @@
+#!/usr/bin/python
+# coding: utf8
+# /*##########################################################################
+#
+# Copyright (c) 2015-2017 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+
+__authors__ = ["Jérôme Kieffer", "Thomas Vincent"]
+__date__ = "04/05/2017"
+__license__ = "MIT"
+
+
+# This import is here only to fix a bug on Debian 7 with python2.7
+# Without this, the system io module is not loaded from numpy.distutils
+# the silx.io module seems to be loaded instead
+import io
+
+import sys
+import os
+import platform
+import shutil
+import logging
+import glob
+
+logging.basicConfig(level=logging.INFO)
+
+logger = logging.getLogger("silx.setup")
+
+
+from distutils.command.clean import clean as Clean
+from distutils.command.build import build as _build
+try:
+ from setuptools import Command
+ from setuptools.command.build_py import build_py as _build_py
+ from setuptools.command.build_ext import build_ext
+ from setuptools.command.sdist import sdist
+ logger.info("Use setuptools")
+except ImportError:
+ try:
+ from numpy.distutils.core import Command
+ except ImportError:
+ from distutils.core import Command
+ from distutils.command.build_py import build_py as _build_py
+ from distutils.command.build_ext import build_ext
+ from distutils.command.sdist import sdist
+ logger.info("Use distutils")
+
+try:
+ import sphinx
+ import sphinx.util.console
+ sphinx.util.console.color_terminal = lambda: False
+ from sphinx.setup_command import BuildDoc
+except ImportError:
+ sphinx = None
+
+
+PROJECT = "silx"
+
+if "LANG" not in os.environ and sys.platform == "darwin" and sys.version_info[0] > 2:
+ print("""WARNING: the LANG environment variable is not defined,
+an utf-8 LANG is mandatory to use setup.py, you may face unexpected UnicodeError.
+export LANG=en_US.utf-8
+export LC_ALL=en_US.utf-8
+""")
+
+
+def get_version():
+ """Returns current version number from version.py file"""
+ import version
+ return version.strictversion
+
+
+def get_readme():
+ """Returns content of README.rst file"""
+ dirname = os.path.dirname(os.path.abspath(__file__))
+ filename = os.path.join(dirname, "README.rst")
+ with io.open(filename, "r", encoding="utf-8") as fp:
+ long_description = fp.read()
+ return long_description
+
+
+classifiers = ["Development Status :: 4 - Beta",
+ "Environment :: Console",
+ "Environment :: MacOS X",
+ "Environment :: Win32 (MS Windows)",
+ "Environment :: X11 Applications :: Qt",
+ "Intended Audience :: Education",
+ "Intended Audience :: Science/Research",
+ "License :: OSI Approved :: MIT License",
+ "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
+ "Natural Language :: English",
+ "Operating System :: MacOS",
+ "Operating System :: Microsoft :: Windows",
+ "Operating System :: POSIX",
+ "Programming Language :: Cython",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Topic :: Scientific/Engineering :: Physics",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ ]
+
+
+# ########## #
+# version.py #
+# ########## #
+
+class build_py(_build_py):
+ """
+ Enhanced build_py which copies version.py to <PROJECT>._version.py
+ """
+ def find_package_modules(self, package, package_dir):
+ modules = _build_py.find_package_modules(self, package, package_dir)
+ if package == PROJECT:
+ modules.append((PROJECT, '_version', 'version.py'))
+ return modules
+
+
+########
+# Test #
+########
+
+class PyTest(Command):
+ """Command to start tests running the script: run_tests.py -i"""
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ import subprocess
+ errno = subprocess.call([sys.executable, 'run_tests.py', '-i'])
+ if errno != 0:
+ raise SystemExit(errno)
+
+
+# ################### #
+# build_doc command #
+# ################### #
+
+if sphinx is None:
+ class SphinxExpectedCommand(Command):
+ """Command to inform that sphinx is missing"""
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ raise RuntimeError(
+ 'Sphinx is required to build or test the documentation.\n'
+ 'Please install Sphinx (http://www.sphinx-doc.org).')
+
+
+class BuildMan(Command):
+ """Command to build man pages"""
+ user_options = []
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ build = self.get_finalized_command('build')
+ path = sys.path
+ path.insert(0, os.path.abspath(build.build_lib))
+
+ env = dict((str(k), str(v)) for k, v in os.environ.items())
+ env["PYTHONPATH"] = os.pathsep.join(path)
+
+ import subprocess
+
+ status = subprocess.call(["mkdir", "-p", "build/man"])
+ if status != 0:
+ raise RuntimeError("Fail to create build/man directory")
+
+ try:
+ import tempfile
+ import stat
+ script_name = None
+
+ # help2man expect a single executable file to extract the help
+ # we create it, execute it, and delete it at the end
+
+ # create a launcher using the right python interpreter
+ script_fid, script_name = tempfile.mkstemp(prefix="%s_" % PROJECT, text=True)
+ script = os.fdopen(script_fid, 'wt')
+ script.write("#!%s\n" % sys.executable)
+ script.write("import runpy\n")
+ script.write("runpy.run_module('%s', run_name='__main__')\n" % PROJECT)
+ script.close()
+
+ # make it executable
+ mode = os.stat(script_name).st_mode
+ os.chmod(script_name, mode + stat.S_IEXEC)
+
+ # execute help2man
+ p = subprocess.Popen(["help2man", script_name, "-o", "build/man/silx.1"], env=env)
+ status = p.wait()
+ if status != 0:
+ raise RuntimeError("Fail to generate man documentation")
+ finally:
+ # clean up the script
+ if script_name is not None:
+ os.remove(script_name)
+
+
+if sphinx is not None:
+ class BuildDocCommand(BuildDoc):
+ """Command to build documentation using sphinx.
+
+ Project should have already be built.
+ """
+
+ def run(self):
+ # make sure the python path is pointing to the newly built
+ # code so that the documentation is built on this and not a
+ # previously installed version
+
+ build = self.get_finalized_command('build')
+ sys.path.insert(0, os.path.abspath(build.build_lib))
+
+ # # Copy .ui files to the path:
+ # dst = os.path.join(
+ # os.path.abspath(build.build_lib), "silx", "gui")
+ # if not os.path.isdir(dst):
+ # os.makedirs(dst)
+ # for i in os.listdir("gui"):
+ # if i.endswith(".ui"):
+ # src = os.path.join("gui", i)
+ # idst = os.path.join(dst, i)
+ # if not os.path.exists(idst):
+ # shutil.copy(src, idst)
+
+ # Build the Users Guide in HTML and TeX format
+ for builder in ['html', 'latex']:
+ self.builder = builder
+ self.builder_target_dir = os.path.join(self.build_dir, builder)
+ self.mkpath(self.builder_target_dir)
+ BuildDoc.run(self)
+ sys.path.pop(0)
+else:
+ BuildDocCommand = SphinxExpectedCommand
+
+
+# ################### #
+# test_doc command #
+# ################### #
+
+if sphinx is not None:
+ class TestDocCommand(BuildDoc):
+ """Command to test the documentation using sphynx doctest.
+
+ http://www.sphinx-doc.org/en/1.4.8/ext/doctest.html
+ """
+ def run(self):
+ # make sure the python path is pointing to the newly built
+ # code so that the documentation is built on this and not a
+ # previously installed version
+
+ build = self.get_finalized_command('build')
+ sys.path.insert(0, os.path.abspath(build.build_lib))
+
+ # Build the Users Guide in HTML and TeX format
+ for builder in ['doctest']:
+ self.builder = builder
+ self.builder_target_dir = os.path.join(self.build_dir, builder)
+ self.mkpath(self.builder_target_dir)
+ BuildDoc.run(self)
+ sys.path.pop(0)
+
+else:
+ TestDocCommand = SphinxExpectedCommand
+
+# ############################# #
+# numpy.distutils Configuration #
+# ############################# #
+
+def configuration(parent_package='', top_path=None):
+ """Recursive construction of package info to be used in setup().
+
+ See http://docs.scipy.org/doc/numpy/reference/distutils.html#numpy.distutils.misc_util.Configuration
+ """
+ try:
+ from numpy.distutils.misc_util import Configuration
+ except ImportError:
+ raise ImportError(
+ "To install this package, you must install numpy first\n"
+ "(See https://pypi.python.org/pypi/numpy)")
+ config = Configuration(None, parent_package, top_path)
+ config.set_options(
+ ignore_setup_xxx_py=True,
+ assume_default_configuration=True,
+ delegate_options_to_subpackages=True,
+ quiet=True)
+ config.add_subpackage(PROJECT)
+ return config
+
+# ############## #
+# Compiler flags #
+# ############## #
+
+
+class Build(_build):
+ """Command to support more user options for the build."""
+
+ user_options = [
+ ('no-openmp', None,
+ "do not use OpenMP for compiled extension modules"),
+ ('openmp', None,
+ "use OpenMP for the compiled extension modules"),
+ ('no-cython', None,
+ "do not compile Cython extension modules (use default compiled c-files)"),
+ ('force-cython', None,
+ "recompile all Cython extension modules"),
+ ]
+ user_options.extend(_build.user_options)
+
+ boolean_options = ['no-openmp', 'openmp', 'no-cython', 'force-cython']
+ boolean_options.extend(_build.boolean_options)
+
+ def initialize_options(self):
+ _build.initialize_options(self)
+ self.no_openmp = None
+ self.openmp = None
+ self.no_cython = None
+ self.force_cython = None
+
+ def finalize_options(self):
+ _build.finalize_options(self)
+ self.finalize_cython_options(min_version='0.21.1')
+ self.finalize_openmp_options()
+
+ def _parse_env_as_bool(self, key):
+ content = os.environ.get(key, "")
+ value = content.lower()
+ if value in ["1", "true", "yes", "y"]:
+ return True
+ if value in ["0", "false", "no", "n"]:
+ return False
+ if value in ["none", ""]:
+ return None
+ msg = "Env variable '%s' contains '%s'. But a boolean or an empty \
+ string was expected. Variable ignored."
+ logger.warning(msg, key, content)
+ return None
+
+ def finalize_openmp_options(self):
+ """Check if extensions must be compiled with OpenMP.
+
+ The result is stored into the object.
+ """
+ if self.openmp:
+ use_openmp = True
+ elif self.no_openmp:
+ use_openmp = False
+ else:
+ env_force_cython = self._parse_env_as_bool("WITH_OPENMP")
+ if env_force_cython is not None:
+ use_openmp = env_force_cython
+ else:
+ # Use it by default
+ use_openmp = True
+
+ if use_openmp:
+ if platform.system() == "Darwin":
+ # By default Xcode5 & XCode6 do not support OpenMP, Xcode4 is OK.
+ osx = tuple([int(i) for i in platform.mac_ver()[0].split(".")])
+ if osx >= (10, 8):
+ logger.warning("OpenMP support ignored. Your platform do not support it")
+ use_openmp = False
+
+ # Remove attributes used by distutils parsing
+ # use 'use_openmp' instead
+ del self.no_openmp
+ del self.openmp
+ self.use_openmp = use_openmp
+
+ def finalize_cython_options(self, min_version=None):
+ """
+ Check if cythonization must be used for the extensions.
+
+ The result is stored into the object.
+ """
+
+ if self.force_cython:
+ use_cython = "force"
+ elif self.no_cython:
+ use_cython = "no"
+ else:
+ env_force_cython = self._parse_env_as_bool("FORCE_CYTHON")
+ env_with_cython = self._parse_env_as_bool("WITH_CYTHON")
+ if env_force_cython is True:
+ use_cython = "force"
+ elif env_with_cython is True:
+ use_cython = "yes"
+ elif env_with_cython is False:
+ use_cython = "no"
+ else:
+ # Use it by default
+ use_cython = "yes"
+
+ if use_cython in ["force", "yes"]:
+ try:
+ import Cython.Compiler.Version
+ if min_version and Cython.Compiler.Version.version < min_version:
+ msg = "Cython version is too old. At least version is %s \
+ expected. Cythonization is skipped."
+ logger.warning(msg, str(min_version))
+ use_cython = "no"
+ except ImportError:
+ msg = "Cython is not available. Cythonization is skipped."
+ logger.warning(msg)
+ use_cython = "no"
+
+
+ # Remove attribute used by distutils parsing
+ # use 'use_cython' and 'force_cython' instead
+ del self.no_cython
+ self.force_cython = use_cython == "force"
+ self.use_cython = use_cython in ["force", "yes"]
+
+
+class BuildExt(build_ext):
+ """Handle extension compilation.
+
+ Command-line argument and environment can custom:
+
+ - The use of cython to cythonize files, else a default version is used
+ - Build extension with support of OpenMP (by default it is enabled)
+ - If building with MSVC, compiler flags are converted from gcc flags.
+ """
+
+ COMPILE_ARGS_CONVERTER = {'-fopenmp': '/openmp'}
+
+ LINK_ARGS_CONVERTER = {'-fopenmp': ' '}
+
+ description = 'Build silx extensions'
+
+ def finalize_options(self):
+ build_ext.finalize_options(self)
+ build_obj = self.distribution.get_command_obj("build")
+ self.use_openmp = build_obj.use_openmp
+ self.use_cython = build_obj.use_cython
+ self.force_cython = build_obj.force_cython
+
+ def patch_with_default_cythonized_files(self, ext):
+ """Replace cython files by .c or .cpp files in extension's sources.
+
+ It replaces the *.pyx and *.py source files of the extensions
+ to either *.cpp or *.c source files.
+ No compilation is performed.
+
+ :param Extension ext: An extension to patch.
+ """
+ new_sources = []
+ for source in ext.sources:
+ base, file_ext = os.path.splitext(source)
+ if file_ext in ('.pyx', '.py'):
+ if ext.language == 'c++':
+ cythonized = base + '.cpp'
+ else:
+ cythonized = base + '.c'
+ if not os.path.isfile(cythonized):
+ raise RuntimeError("Source file not found: %s. Cython is needed" % cythonized)
+ print("Use default cythonized file for %s" % source)
+ new_sources.append(cythonized)
+ else:
+ new_sources.append(source)
+ ext.sources = new_sources
+
+ def patch_extension(self, ext):
+ """
+ Patch an extension according to requested Cython and OpenMP usage.
+
+ :param Extension ext: An extension
+ """
+ # Cytonize
+ if not self.use_cython:
+ self.patch_with_default_cythonized_files(ext)
+ else:
+ from Cython.Build import cythonize
+ patched_exts = cythonize(
+ [ext],
+ compiler_directives={'embedsignature': True},
+ force=self.force_cython,
+ compile_time_env={"HAVE_OPENMP": self.use_openmp}
+ )
+ ext.sources = patched_exts[0].sources
+
+ # Remove OpenMP flags if OpenMP is disabled
+ if not self.use_openmp:
+ ext.extra_compile_args = [
+ f for f in ext.extra_compile_args if f != '-fopenmp']
+ ext.extra_link_args = [
+ f for f in ext.extra_link_args if f != '-fopenmp']
+
+ # Convert flags from gcc to MSVC if required
+ if self.compiler.compiler_type == 'msvc':
+ ext.extra_compile_args = [self.COMPILE_ARGS_CONVERTER.get(f, f)
+ for f in ext.extra_compile_args]
+ ext.extra_link_args = [self.LINK_ARGS_CONVERTER.get(f, f)
+ for f in ext.extra_link_args]
+
+ def build_extensions(self):
+ for ext in self.extensions:
+ self.patch_extension(ext)
+ build_ext.build_extensions(self)
+
+
+################################################################################
+# Clean command
+################################################################################
+
+
+class CleanCommand(Clean):
+ description = "Remove build artifacts from the source tree"
+
+ def expand(self, path_list):
+ """Expand a list of path using glob magic.
+
+ :param list[str] path_list: A list of path which may contains magic
+ :rtype: list[str]
+ :returns: A list of path without magic
+ """
+ path_list2 = []
+ for path in path_list:
+ if glob.has_magic(path):
+ iterator = glob.iglob(path)
+ path_list2.extend(iterator)
+ else:
+ path_list2.append(path)
+ return path_list2
+
+ def run(self):
+ Clean.run(self)
+ # really remove the directories
+ # and not only if they are empty
+ to_remove = [self.build_base]
+ to_remove = self.expand(to_remove)
+
+ if not self.dry_run:
+ for path in to_remove:
+ try:
+ if os.path.isdir(path):
+ shutil.rmtree(path)
+ else:
+ os.remove(path)
+ logger.info("removing '%s'", path)
+ except OSError:
+ pass
+
+################################################################################
+# Debian source tree
+################################################################################
+
+
+class sdist_debian(sdist):
+ """
+ Tailor made sdist for debian
+ * remove auto-generated doc
+ * remove cython generated .c files
+ * remove cython generated .c files
+ * remove .bat files
+ * include .l man files
+ """
+ @staticmethod
+ def get_debian_name():
+ import version
+ name = "%s_%s" % (PROJECT, version.debianversion)
+ return name
+
+ def prune_file_list(self):
+ sdist.prune_file_list(self)
+ to_remove = ["doc/build", "doc/pdf", "doc/html", "pylint", "epydoc"]
+ print("Removing files for debian")
+ for rm in to_remove:
+ self.filelist.exclude_pattern(pattern="*", anchor=False, prefix=rm)
+
+ # this is for Cython files specifically: remove C & html files
+ search_root = os.path.dirname(os.path.abspath(__file__))
+ for root, _, files in os.walk(search_root):
+ for afile in files:
+ if os.path.splitext(afile)[1].lower() == ".pyx":
+ base_file = os.path.join(root, afile)[len(search_root) + 1:-4]
+ self.filelist.exclude_pattern(pattern=base_file + ".c")
+ self.filelist.exclude_pattern(pattern=base_file + ".cpp")
+ self.filelist.exclude_pattern(pattern=base_file + ".html")
+
+ # do not include third_party/_local files
+ self.filelist.exclude_pattern(pattern="*", prefix="silx/third_party/_local")
+
+ def make_distribution(self):
+ self.prune_file_list()
+ sdist.make_distribution(self)
+ dest = self.archive_files[0]
+ dirname, basename = os.path.split(dest)
+ base, ext = os.path.splitext(basename)
+ while ext in [".zip", ".tar", ".bz2", ".gz", ".Z", ".lz", ".orig"]:
+ base, ext = os.path.splitext(base)
+ if ext:
+ dest = "".join((base, ext))
+ else:
+ dest = base
+ # sp = dest.split("-")
+ # base = sp[:-1]
+ # nr = sp[-1]
+ debian_arch = os.path.join(dirname, self.get_debian_name() + ".orig.tar.gz")
+ os.rename(self.archive_files[0], debian_arch)
+ self.archive_files = [debian_arch]
+ print("Building debian .orig.tar.gz in %s" % self.archive_files[0])
+
+
+# ##### #
+# setup #
+# ##### #
+
+def get_project_configuration(dry_run):
+ """Returns project arguments for setup"""
+ install_requires = [
+ # for most of the computation
+ "numpy",
+ # for the script launcher
+ "setuptools"]
+
+ setup_requires = ["setuptools", "numpy"]
+
+ package_data = {
+ 'silx.resources': [
+ # Add here all resources files
+ 'gui/icons/*.png',
+ 'gui/icons/*.svg',
+ 'gui/icons/*.mng',
+ 'gui/icons/*.gif',
+ 'gui/icons/animated/*.png',
+ 'opencl/*.cl',
+ 'opencl/sift/*.cl']
+ }
+
+ entry_points = {
+ 'console_scripts': ['silx = silx.__main__:main'],
+ # 'gui_scripts': [],
+ }
+
+ cmdclass = dict(
+ build=Build,
+ build_py=build_py,
+ test=PyTest,
+ build_doc=BuildDocCommand,
+ test_doc=TestDocCommand,
+ build_ext=BuildExt,
+ build_man=BuildMan,
+ clean=CleanCommand,
+ debian_src=sdist_debian)
+
+ if dry_run:
+ # DRY_RUN implies actions which do not require NumPy
+ #
+ # And they are required to succeed without Numpy for example when
+ # pip is used to install silx when Numpy is not yet present in
+ # the system.
+ setup_kwargs = {}
+ else:
+ config = configuration()
+ setup_kwargs = config.todict()
+
+ setup_kwargs.update(name=PROJECT,
+ version=get_version(),
+ url="https://github.com/silx-kit/silx",
+ author="data analysis unit",
+ author_email="silx@esrf.fr",
+ classifiers=classifiers,
+ description="Software library for X-Ray data analysis",
+ long_description=get_readme(),
+ install_requires=install_requires,
+ setup_requires=setup_requires,
+ cmdclass=cmdclass,
+ package_data=package_data,
+ zip_safe=False,
+ entry_points=entry_points,
+ )
+ return setup_kwargs
+
+
+def setup_package():
+ """Run setup(**kwargs)
+
+ Depending on the command, it either runs the complete setup which depends on numpy,
+ or a *dry run* setup with no dependency on numpy.
+ """
+
+ # Check if action requires build/install
+ dry_run = len(sys.argv) == 1 or (len(sys.argv) >= 2 and (
+ '--help' in sys.argv[1:] or
+ sys.argv[1] in ('--help-commands', 'egg_info', '--version',
+ 'clean', '--name')))
+
+ if dry_run:
+ # DRY_RUN implies actions which do not require dependancies, like NumPy
+ try:
+ from setuptools import setup
+ logger.info("Use setuptools.setup")
+ except ImportError:
+ from distutils.core import setup
+ logger.info("Use distutils.core.setup")
+ else:
+ try:
+ from setuptools import setup
+ except ImportError:
+ from numpy.distutils.core import setup
+ logger.info("Use numpydistutils.setup")
+
+ setup_kwargs = get_project_configuration(dry_run)
+ setup(**setup_kwargs)
+
+if __name__ == "__main__":
+ setup_package()