diff --git a/libraries/python2-matplotlib/README b/libraries/python2-matplotlib/README new file mode 100644 index 0000000000..ef592d939f --- /dev/null +++ b/libraries/python2-matplotlib/README @@ -0,0 +1,26 @@ +Matplotlib strives to produce publication quality 2D graphics for +interactive graphing, scientific publishing, user interface development +and web application servers targeting multiple user interfaces and +hardcopy output formats. The 'pylab' mode of ipython uses matplotlib +to emulate matlab graphics. + +OPTIONAL DEPENDENCIES: pytest, mock, ffmpeg/avconv, Inkscape, + wxPython3 + +NOTES: The optional wxagg backend is disabled by default because + of possible issues related to wxPython versions. If you wish to + use this backend install wxPython3 in advance and set + DISABLE_WXAGG to "no" when executing the SlackBuild. + + IMPORTANT: There is some interaction between wxPython and + wxPython3, pay attention to the corresponding + README files. + + If you want to enable the optional subpackages tests and + toolkits_tests you need to set the variable ENABLE_TESTS to + "yes" when executing the SlackBuild and make sure that pytest, + mock, ffmpeg/avconv and Inkscape are already installed. Besides + ffmpeg/avconv are also needed if you wish better support of + animation output formats. + + Version 2.2.5 is the last available for python2. diff --git a/libraries/python2-matplotlib/python2-matplotlib.SlackBuild b/libraries/python2-matplotlib/python2-matplotlib.SlackBuild new file mode 100644 index 0000000000..ea329da139 --- /dev/null +++ b/libraries/python2-matplotlib/python2-matplotlib.SlackBuild @@ -0,0 +1,111 @@ +#!/bin/bash + +# Slackware build script for matplotlib + +# Copyright 2011-2022 Serban Udrea +# All rights reserved. +# +# Redistribution and use of this script, with or without modification, +# is permitted provided that the following conditions are met: +# +# 1. Redistributions of this script must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Written by Aleksandar Samardzic +# Updated to 0.99.1.2 by João Felipe Santos +# Patch for libpng borrowed from Arch Linux (no longer needed, 2016.08.28, S.U.) +# Updated up to version 2.2.5 by Serban Udrea + +cd $(dirname $0) ; CWD=$(pwd) + +PRGNAM=python2-matplotlib +SRCNAM=matplotlib +VERSION=${VERSION:-2.2.5} +BUILD=${BUILD:-1} +TAG=${TAG:-_SBo} +PKGTYPE=${PKGTYPE:-tgz} + +TARBALL_VERSION=${TARBALL_VERSION:-2.2.5} # Tarball may have wrong versioning +ENABLE_TESTS=${ENABLE_TESTS:-N} +DISABLE_WXAGG=${DISABLE_WXAGG:-Y} + +if [ -z "$ARCH" ]; then + case "$( uname -m )" in + i?86) ARCH=i586 ;; + arm*) ARCH=arm ;; + *) ARCH=$( uname -m ) ;; + esac +fi + +# If the variable PRINT_PACKAGE_NAME is set, then this script will report what +# the name of the created package would be, and then exit. This information +# could be useful to other scripts. +if [ ! -z "${PRINT_PACKAGE_NAME}" ]; then + echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE" + exit 0 +fi + +TMP=${TMP:-/tmp/SBo} +PKG=${PKG:-$TMP/package-$PRGNAM} +OUTPUT=${OUTPUT:-/tmp} + +set -e + +rm -rf $PKG +mkdir -p $TMP $PKG $OUTPUT +cd $TMP +rm -rf $SRCNAM-$TARBALL_VERSION +tar xvf $CWD/$SRCNAM-$VERSION.tar.gz +cd $SRCNAM-$TARBALL_VERSION +chown -R root:root . +find -L . \ + \( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \ + -o -perm 511 \) -exec chmod 755 {} \; -o \ + \( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \ + -o -perm 440 -o -perm 400 \) -exec chmod 644 {} \; + +ENABLE_TESTS=$(echo "$ENABLE_TESTS"|cut -b 1|tr a-z A-Z) +DISABLE_WXAGG=$(echo "$DISABLE_WXAGG"|cut -b 1|tr a-z A-Z) +cat setup.cfg.template > setup.cfg + +if [ "$ENABLE_TESTS" = "Y" ] +then + sed -i "s|#tests = False|tests = True|" setup.cfg +fi + +if [ "$DISABLE_WXAGG" = "Y" ] +then + sed -i "s|#wxagg = auto|wxagg = False|" setup.cfg +fi + +# Use modified setupext.py to make sure that the build process gets +# interrupted if requirements are not fulfilled +# +cat "${CWD}/setupext.py" > setupext.py + +python2 setup.py install --root $PKG # > ${CWD}/SETUP.OUTPUT 2>&1 + +find $PKG | xargs file | grep -e "executable" -e "shared object" | grep ELF \ + | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true + +mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION +cp -a INSTALL.rst MANIFEST.in README.rst PKG-INFO $PKG/usr/doc/$PRGNAM-$VERSION +cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild + +mkdir -p $PKG/install +cat $CWD/slack-desc > $PKG/install/slack-desc + +cd $PKG +/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE diff --git a/libraries/python2-matplotlib/python2-matplotlib.info b/libraries/python2-matplotlib/python2-matplotlib.info new file mode 100644 index 0000000000..c7893768f3 --- /dev/null +++ b/libraries/python2-matplotlib/python2-matplotlib.info @@ -0,0 +1,10 @@ +PRGNAM="python2-matplotlib" +VERSION="2.2.5" +HOMEPAGE="https://matplotlib.org/" +DOWNLOAD="https://files.pythonhosted.org/packages/10/5f/10c310c943f29e67976dcc26dccf9305a5a9bc7483e631ee74a0f95aa5b2/matplotlib-2.2.5.tar.gz" +MD5SUM="422fc58e7323b7ba3f19382424f9ab7b" +DOWNLOAD_x86_64="" +MD5SUM_x86_64="" +REQUIRES="python2-numpy python2-dateutil pytz python2-cycler functools-lru-cache subprocess32 python2-kiwisolver tornado" +MAINTAINER="Serban Udrea" +EMAIL="S.Udrea@gsi.de" diff --git a/libraries/python2-matplotlib/setupext.py b/libraries/python2-matplotlib/setupext.py new file mode 100644 index 0000000000..bf931378d6 --- /dev/null +++ b/libraries/python2-matplotlib/setupext.py @@ -0,0 +1,2249 @@ +from __future__ import print_function, absolute_import + +from importlib import import_module + +from distutils import sysconfig +from distutils import version +from distutils.core import Extension +import hashlib + +import distutils.command.build_ext +from io import BytesIO + +import glob +import multiprocessing +import os +import platform + +import re + +import setuptools +import shutil + +import subprocess +from subprocess import check_output +import sys +import warnings +from textwrap import fill +import shutil + +import tarfile +import textwrap + +import versioneer +import warnings +import contextlib + +if sys.version_info < (3, ): + from urllib2 import urlopen, Request + + class FileExistsError(OSError): + pass + + def makedirs(path, exist_ok=True): + if not exist_ok: + raise ValueError("this backport only supports exist_ok is True") + if not path or os.path.exists(path): + return + head, tail = os.path.split(path) + + makedirs(head, exist_ok=True) + os.makedirs(path) + +else: + from urllib.request import urlopen, Request + from os import makedirs + + +PY3min = (sys.version_info[0] >= 3) + + +def _get_xdg_cache_dir(): + """ + Return the XDG cache directory. + + See https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + """ + cache_dir = os.environ.get('XDG_CACHE_HOME') + if not cache_dir: + cache_dir = os.path.expanduser('~/.cache') + if cache_dir.startswith('~/'): # Expansion failed. + return None + return os.path.join(cache_dir, 'matplotlib') + + +def get_fd_hash(fd): + """ + Compute the sha256 hash of the bytes in a file-like + """ + BLOCKSIZE = 1 << 16 + hasher = hashlib.sha256() + old_pos = fd.tell() + fd.seek(0) + buf = fd.read(BLOCKSIZE) + while buf: + hasher.update(buf) + buf = fd.read(BLOCKSIZE) + fd.seek(old_pos) + return hasher.hexdigest() + + +def download_or_cache(url, sha): + """ + Get bytes from the given url or local cache. + + Parameters + ---------- + url : str + The url to download + + sha : str + The sha256 of the file + + Returns + ------- + BytesIO + The file loaded into memory. + """ + cache_dir = _get_xdg_cache_dir() + + def get_from_cache(local_fn): + if cache_dir is None: + raise Exception("no cache dir") + cache_filename = os.path.join(cache_dir, local_fn) + with open(cache_filename, 'rb') as fin: + buf = BytesIO(fin.read()) + file_sha = get_fd_hash(buf) + if file_sha != sha: + return None + buf.seek(0) + return buf + + def write_cache(local_fn, data): + if cache_dir is None: + raise Exception("no cache dir") + + cache_filename = os.path.join(cache_dir, local_fn) + makedirs(cache_dir, exist_ok=True) + if sys.version_info < (3, ): + if os.path.exists(cache_filename): + raise FileExistsError + mode = 'wb' + else: + mode = 'xb' + old_pos = data.tell() + data.seek(0) + with open(cache_filename, mode=mode) as fout: + fout.write(data.read()) + data.seek(old_pos) + + try: + return get_from_cache(sha) + except Exception: + pass + + # jQueryUI's website blocks direct downloads from urllib.request's + # default User-Agent, but not (for example) wget; so I don't feel too + # bad passing in an empty User-Agent. + with contextlib.closing(urlopen( + Request(url, headers={"User-Agent": ""}))) as req: + file_contents = BytesIO(req.read()) + file_contents.seek(0) + + file_sha = get_fd_hash(file_contents) + + if file_sha != sha: + raise Exception(("The download file does not match the " + "expected sha. {url} was expected to have " + "{sha} but it had {file_sha}").format( + sha=sha, file_sha=file_sha, url=url)) + + try: + write_cache(sha, file_contents) + except Exception: + pass + + file_contents.seek(0) + return file_contents + + +# SHA256 hashes of the FreeType tarballs +_freetype_hashes = { + '2.6.1': '0a3c7dfbda6da1e8fce29232e8e96d987ababbbf71ebc8c75659e4132c367014', + '2.6.2': '8da42fc4904e600be4b692555ae1dcbf532897da9c5b9fb5ebd3758c77e5c2d4', + '2.6.3': '7942096c40ee6fea882bd4207667ad3f24bff568b96b10fd3885e11a7baad9a3', + '2.6.4': '27f0e38347a1850ad57f84fc4dfed68ba0bc30c96a6fa6138ef84d485dd9a8d7', + '2.6.5': '3bb24add9b9ec53636a63ea8e867ed978c4f8fdd8f1fa5ccfd41171163d4249a', + '2.7': '7b657d5f872b0ab56461f3bd310bd1c5ec64619bd15f0d8e08282d494d9cfea4', + '2.7.1': '162ef25aa64480b1189cdb261228e6c5c44f212aac4b4621e28cf2157efb59f5', + '2.8': '33a28fabac471891d0523033e99c0005b95e5618dc8ffa7fa47f9dadcacb1c9b', + '2.8.1': '876711d064a6a1bd74beb18dd37f219af26100f72daaebd2d86cb493d7cd7ec6', +} +# This is the version of FreeType to use when building a local +# version. It must match the value in +# lib/matplotlib.__init__.py and also needs to be changed below in the +# embedded windows build script (grep for "REMINDER" in this file) +LOCAL_FREETYPE_VERSION = '2.6.1' +LOCAL_FREETYPE_HASH = _freetype_hashes.get(LOCAL_FREETYPE_VERSION, 'unknown') + +if sys.platform != 'win32': + if not PY3min: + from commands import getstatusoutput + else: + from subprocess import getstatusoutput + + +if PY3min: + import configparser +else: + import ConfigParser as configparser + + +# matplotlib build options, which can be altered using setup.cfg +options = { + 'display_status': True, + 'verbose': False, + 'backend': None, + 'basedirlist': None + } + + +setup_cfg = os.environ.get('MPLSETUPCFG', 'setup.cfg') +if os.path.exists(setup_cfg): + if PY3min: + config = configparser.ConfigParser() + else: + config = configparser.SafeConfigParser() + config.read(setup_cfg) + + if config.has_option('status', 'suppress'): + options['display_status'] = not config.getboolean("status", "suppress") + + if config.has_option('rc_options', 'backend'): + options['backend'] = config.get("rc_options", "backend") + + if config.has_option('directories', 'basedirlist'): + options['basedirlist'] = [ + x.strip() for x in + config.get("directories", "basedirlist").split(',')] + + if config.has_option('test', 'local_freetype'): + options['local_freetype'] = config.getboolean("test", "local_freetype") +else: + config = None + +lft = bool(os.environ.get('MPLLOCALFREETYPE', False)) +options['local_freetype'] = lft or options.get('local_freetype', False) + + +def get_win32_compiler(): + """ + Determine the compiler being used on win32. + """ + # Used to determine mingw32 or msvc + # This is pretty bad logic, someone know a better way? + for v in sys.argv: + if 'mingw32' in v: + return 'mingw32' + return 'msvc' +win32_compiler = get_win32_compiler() + + +def extract_versions(): + """ + Extracts version values from the main matplotlib __init__.py and + returns them as a dictionary. + """ + with open('lib/matplotlib/__init__.py') as fd: + for line in fd.readlines(): + if (line.startswith('__version__numpy__')): + exec(line.strip()) + return locals() + + +def has_include_file(include_dirs, filename): + """ + Returns `True` if `filename` can be found in one of the + directories in `include_dirs`. + """ + if sys.platform == 'win32': + include_dirs = list(include_dirs) # copy before modify + include_dirs += os.environ.get('INCLUDE', '.').split(os.pathsep) + for dir in include_dirs: + if os.path.exists(os.path.join(dir, filename)): + return True + return False + + +def check_include_file(include_dirs, filename, package): + """ + Raises an exception if the given include file can not be found. + """ + if not has_include_file(include_dirs, filename): + raise CheckFailed( + "The C/C++ header for %s (%s) could not be found. You " + "may need to install the development package." % + (package, filename)) + + +def get_base_dirs(): + """ + Returns a list of standard base directories on this platform. + """ + if options['basedirlist']: + return options['basedirlist'] + + if os.environ.get('MPLBASEDIRLIST'): + return os.environ.get('MPLBASEDIRLIST').split(os.pathsep) + + win_bases = ['win32_static', ] + # on conda windows, we also add the \Library, + # as conda installs libs/includes there + # env var names mess: https://github.com/conda/conda/issues/2312 + conda_env_path = os.getenv('CONDA_PREFIX') # conda >= 4.1 + if not conda_env_path: + conda_env_path = os.getenv('CONDA_DEFAULT_ENV') # conda < 4.1 + if conda_env_path and os.path.isdir(conda_env_path): + win_bases.append(os.path.join(conda_env_path, "Library")) + + basedir_map = { + 'win32': win_bases, + 'darwin': ['/usr/local/', '/usr', '/usr/X11', + '/opt/X11', '/opt/local'], + 'sunos5': [os.getenv('MPLIB_BASE') or '/usr/local', ], + 'gnu0': ['/usr'], + 'aix5': ['/usr/local'], + } + return basedir_map.get(sys.platform, ['/usr/local', '/usr']) + + +def get_include_dirs(): + """ + Returns a list of standard include directories on this platform. + """ + include_dirs = [os.path.join(d, 'include') for d in get_base_dirs()] + if sys.platform != 'win32': + # gcc includes this dir automatically, so also look for headers in + # these dirs + include_dirs.extend( + os.environ.get('CPLUS_INCLUDE_PATH', '').split(os.pathsep)) + return include_dirs + + +def is_min_version(found, minversion): + """ + Returns `True` if `found` is at least as high a version as + `minversion`. + """ + expected_version = version.LooseVersion(minversion) + found_version = version.LooseVersion(found) + return found_version >= expected_version + + +# Define the display functions only if display_status is True. +if options['display_status']: + def print_line(char='='): + print(char * 76) + + def print_status(package, status): + initial_indent = "%22s: " % package + indent = ' ' * 24 + print(fill(str(status), width=76, + initial_indent=initial_indent, + subsequent_indent=indent)) + + def print_message(message): + indent = ' ' * 24 + "* " + print(fill(str(message), width=76, + initial_indent=indent, + subsequent_indent=indent)) + + def print_raw(section): + print(section) +else: + def print_line(*args, **kwargs): + pass + print_status = print_message = print_raw = print_line + + +# Remove the -Wstrict-prototypes option, is it's not valid for C++ +customize_compiler = distutils.command.build_ext.customize_compiler + + +def my_customize_compiler(compiler): + retval = customize_compiler(compiler) + try: + compiler.compiler_so.remove('-Wstrict-prototypes') + except (ValueError, AttributeError): + pass + return retval + +distutils.command.build_ext.customize_compiler = my_customize_compiler + + +def make_extension(name, files, *args, **kwargs): + """ + Make a new extension. Automatically sets include_dirs and + library_dirs to the base directories appropriate for this + platform. + + `name` is the name of the extension. + + `files` is a list of source files. + + Any additional arguments are passed to the + `distutils.core.Extension` constructor. + """ + ext = DelayedExtension(name, files, *args, **kwargs) + for dir in get_base_dirs(): + include_dir = os.path.join(dir, 'include') + if os.path.exists(include_dir): + ext.include_dirs.append(include_dir) + for lib in ('lib', 'lib64'): + lib_dir = os.path.join(dir, lib) + if os.path.exists(lib_dir): + ext.library_dirs.append(lib_dir) + ext.include_dirs.append('.') + + return ext + + +def get_buffer_hash(fd): + BLOCKSIZE = 1 << 16 + hasher = hashlib.sha256() + buf = fd.read(BLOCKSIZE) + while buf: + hasher.update(buf) + buf = fd.read(BLOCKSIZE) + + return hasher.hexdigest() + + +class PkgConfig(object): + """ + This is a class for communicating with pkg-config. + """ + def __init__(self): + """ + Determines whether pkg-config exists on this machine. + """ + if sys.platform == 'win32': + self.has_pkgconfig = False + else: + try: + self.pkg_config = os.environ['PKG_CONFIG'] + except KeyError: + self.pkg_config = 'pkg-config' + + self.set_pkgconfig_path() + status, output = getstatusoutput(self.pkg_config + " --help") + self.has_pkgconfig = (status == 0) + if not self.has_pkgconfig: + print("IMPORTANT WARNING:") + print( + " pkg-config is not installed.\n" + " matplotlib may not be able to find some of its dependencies") + + def set_pkgconfig_path(self): + pkgconfig_path = sysconfig.get_config_var('LIBDIR') + if pkgconfig_path is None: + return + + pkgconfig_path = os.path.join(pkgconfig_path, 'pkgconfig') + if not os.path.isdir(pkgconfig_path): + return + + try: + os.environ['PKG_CONFIG_PATH'] += ':' + pkgconfig_path + except KeyError: + os.environ['PKG_CONFIG_PATH'] = pkgconfig_path + + def setup_extension(self, ext, package, default_include_dirs=[], + default_library_dirs=[], default_libraries=[], + alt_exec=None): + """ + Add parameters to the given `ext` for the given `package`. + """ + flag_map = { + '-I': 'include_dirs', '-L': 'library_dirs', '-l': 'libraries'} + + executable = alt_exec + if self.has_pkgconfig: + executable = (self.pkg_config + ' {0}').format(package) + + use_defaults = True + + if executable is not None: + command = "{0} --libs --cflags ".format(executable) + + try: + output = check_output(command, shell=True, + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + pass + else: + output = output.decode(sys.getfilesystemencoding()) + use_defaults = False + for token in output.split(): + attr = flag_map.get(token[:2]) + if attr is not None: + getattr(ext, attr).insert(0, token[2:]) + + if use_defaults: + basedirs = get_base_dirs() + for base in basedirs: + for include in default_include_dirs: + dir = os.path.join(base, include) + if os.path.exists(dir): + ext.include_dirs.append(dir) + for lib in default_library_dirs: + dir = os.path.join(base, lib) + if os.path.exists(dir): + ext.library_dirs.append(dir) + ext.libraries.extend(default_libraries) + return True + + return False + + def get_version(self, package): + """ + Get the version of the package from pkg-config. + """ + if not self.has_pkgconfig: + return None + + status, output = getstatusoutput( + self.pkg_config + " %s --modversion" % (package)) + if status == 0: + return output + return None + + +# The PkgConfig class should be used through this singleton +pkg_config = PkgConfig() + + +class CheckFailed(Exception): + """ + Exception thrown when a `SetupPackage.check` method fails. + """ + pass + + +class SetupPackage(object): + optional = False + pkg_names = { + "apt-get": None, + "yum": None, + "dnf": None, + "brew": None, + "port": None, + "windows_url": None + } + + def check(self): + """ + Checks whether the build dependencies are met. Should raise a + `CheckFailed` exception if the dependency could not be met, otherwise + return a string indicating a version number or some other message + indicating what was found. + """ + pass + + def runtime_check(self): + """ + True if the runtime dependencies of the backend are met. Assumes that + the build-time dependencies are met. + """ + return True + + def get_packages(self): + """ + Get a list of package names to add to the configuration. + These are added to the `packages` list passed to + `distutils.setup`. + """ + return [] + + def get_namespace_packages(self): + """ + Get a list of namespace package names to add to the configuration. + These are added to the `namespace_packages` list passed to + `distutils.setup`. + """ + return [] + + def get_py_modules(self): + """ + Get a list of top-level modules to add to the configuration. + These are added to the `py_modules` list passed to + `distutils.setup`. + """ + return [] + + def get_package_data(self): + """ + Get a package data dictionary to add to the configuration. + These are merged into to the `package_data` list passed to + `distutils.setup`. + """ + return {} + + def get_extension(self): + """ + Get a list of C extensions (`distutils.core.Extension` + objects) to add to the configuration. These are added to the + `extensions` list passed to `distutils.setup`. + """ + return None + + def get_install_requires(self): + """ + Get a list of Python packages that we require. + pip/easy_install will attempt to download and install this + package if it is not installed. + """ + return [] + + def get_setup_requires(self): + """ + Get a list of Python packages that we require at build time. + pip/easy_install will attempt to download and install this + package if it is not installed. + """ + return [] + + def _check_for_pkg_config(self, package, include_file, min_version=None, + version=None): + """ + A convenience function for writing checks for a + pkg_config-defined dependency. + + `package` is the pkg_config package name. + + `include_file` is a top-level include file we expect to find. + + `min_version` is the minimum version required. + + `version` will override the found version if this package + requires an alternate method for that. Set version='unknown' + if the version is not known but you still want to disabled + pkg_config version check. + """ + if version is None: + version = pkg_config.get_version(package) + + if version is None: + raise CheckFailed( + "pkg-config information for '%s' could not be found." % + package) + + if min_version == 'PATCH': + raise CheckFailed( + "Requires patches that have not been merged upstream.") + + if min_version and version != 'unknown': + if (not is_min_version(version, min_version)): + raise CheckFailed( + "Requires %s %s or later. Found %s." % + (package, min_version, version)) + + ext = self.get_extension() + if ext is None: + ext = make_extension('test', []) + pkg_config.setup_extension(ext, package) + + check_include_file( + ext.include_dirs + get_include_dirs(), include_file, package) + + return 'version %s' % version + + def do_custom_build(self): + """ + If a package needs to do extra custom things, such as building a + third-party library, before building an extension, it should + override this method. + """ + pass + + def install_help_msg(self): + """ + Do not override this method ! + + Generate the help message to show if the package is not installed. + To use this in subclasses, simply add the dictionary `pkg_names` as + a class variable: + + pkg_names = { + "apt-get": , + "yum": , + "dnf": , + "brew": , + "port": , + "windows_url": + } + + All the dictionary keys are optional. If a key is not present or has + the value `None` no message is provided for that platform. + """ + def _try_managers(*managers): + for manager in managers: + pkg_name = self.pkg_names.get(manager, None) + if pkg_name: + try: + # `shutil.which()` can be used when Python 2.7 support + # is dropped. It is available in Python 3.3+ + _ = check_output(["which", manager], + stderr=subprocess.STDOUT) + if manager == 'port': + pkgconfig = 'pkgconfig' + else: + pkgconfig = 'pkg-config' + return ('Try installing {0} with `{1} install {2}` ' + 'and pkg-config with `{1} install {3}`' + .format(self.name, manager, pkg_name, + pkgconfig)) + except subprocess.CalledProcessError: + pass + + message = None + if sys.platform == "win32": + url = self.pkg_names.get("windows_url", None) + if url: + message = ('Please check {0} for instructions to install {1}' + .format(url, self.name)) + elif sys.platform == "darwin": + message = _try_managers("brew", "port") + elif sys.platform.startswith("linux"): + release = platform.linux_distribution()[0].lower() + if release in ('debian', 'ubuntu'): + message = _try_managers('apt-get') + elif release in ('centos', 'redhat', 'fedora'): + message = _try_managers('dnf', 'yum') + return message + + +class OptionalPackage(SetupPackage): + optional = True + force = False + config_category = "packages" + default_config = "auto" + + @classmethod + def get_config(cls): + """ + Look at `setup.cfg` and return one of ["auto", True, False] indicating + if the package is at default state ("auto"), forced by the user (case + insensitively defined as 1, true, yes, on for True) or opted-out (case + insensitively defined as 0, false, no, off for False). + """ + conf = cls.default_config + if config is not None and config.has_option(cls.config_category, cls.name): + try: + conf = config.getboolean(cls.config_category, cls.name) + except ValueError: + conf = config.get(cls.config_category, cls.name) + return conf + + def check(self): + """ + Do not override this method! + + For custom dependency checks override self.check_requirements(). + Two things are checked: Configuration file and requirements. + """ + # Check configuration file + conf = self.get_config() + # Default "auto" state or install forced by user + if conf in [True, 'auto']: + message = "installing" + # Set non-optional if user sets `True` in config + if conf is True: + self.optional = False + # Configuration opt-out by user + else: + # Some backend extensions (e.g. Agg) need to be built for certain + # other GUI backends (e.g. TkAgg) even when manually disabled + if self.force is True: + message = "installing forced (config override)" + else: + raise CheckFailed("skipping due to configuration") + + # Check requirements and add extra information (if any) to message. + # If requirements are not met a CheckFailed should be raised in there. + additional_info = self.check_requirements() + if additional_info: + message += ", " + additional_info + + # No CheckFailed raised until now, return install message. + return message + + def check_requirements(self): + """ + Override this method to do custom dependency checks. + + - Raise CheckFailed() if requirements are not met. + - Return message with additional information, or an empty string + (or None) for no additional information. + """ + return "" + + +class OptionalBackendPackage(OptionalPackage): + config_category = "gui_support" + + +class Platform(SetupPackage): + name = "platform" + + def check(self): + return sys.platform + + +class Python(SetupPackage): + name = "python" + + def check(self): + major, minor1, minor2, s, tmp = sys.version_info + + if major < 2: + raise CheckFailed( + "Requires Python 2.7 or later") + elif major == 2 and minor1 < 7: + raise CheckFailed( + "Requires Python 2.7 or later (in the 2.x series)") + elif major == 3 and minor1 < 4: + raise CheckFailed( + "Requires Python 3.4 or later (in the 3.x series)") + + return sys.version + + +class Matplotlib(SetupPackage): + name = "matplotlib" + + def check(self): + return versioneer.get_version() + + def get_packages(self): + return [ + 'matplotlib', + 'matplotlib.backends', + 'matplotlib.backends.qt_editor', + 'matplotlib.compat', + 'matplotlib.projections', + 'matplotlib.axes', + 'matplotlib.sphinxext', + 'matplotlib.style', + 'matplotlib.testing', + 'matplotlib.testing._nose', + 'matplotlib.testing._nose.plugins', + 'matplotlib.testing.jpl_units', + 'matplotlib.tri', + 'matplotlib.cbook' + ] + + def get_py_modules(self): + return ['pylab'] + + def get_package_data(self): + return { + 'matplotlib': + [ + 'mpl-data/fonts/afm/*.afm', + 'mpl-data/fonts/pdfcorefonts/*.afm', + 'mpl-data/fonts/pdfcorefonts/*.txt', + 'mpl-data/fonts/ttf/*.ttf', + 'mpl-data/fonts/ttf/LICENSE_STIX', + 'mpl-data/fonts/ttf/COPYRIGHT.TXT', + 'mpl-data/fonts/ttf/README.TXT', + 'mpl-data/fonts/ttf/RELEASENOTES.TXT', + 'mpl-data/images/*.xpm', + 'mpl-data/images/*.svg', + 'mpl-data/images/*.gif', + 'mpl-data/images/*.pdf', + 'mpl-data/images/*.png', + 'mpl-data/images/*.ppm', + 'mpl-data/example/*.npy', + 'mpl-data/matplotlibrc', + 'backends/web_backend/*.*', + 'backends/web_backend/js/*.*', + 'backends/web_backend/jquery/js/*.min.js', + 'backends/web_backend/jquery/css/themes/base/*.min.css', + 'backends/web_backend/jquery/css/themes/base/images/*', + 'backends/web_backend/jquery-ui-*/*', + 'backends/web_backend/jquery-ui-*/*/*', + 'backends/web_backend/jquery-ui-*/*/*/*', + 'backends/web_backend/css/*.*', + 'backends/Matplotlib.nib/*', + 'mpl-data/stylelib/*.mplstyle', + ]} + + +class SampleData(OptionalPackage): + """ + This handles the sample data that ships with matplotlib. It is + technically optional, though most often will be desired. + """ + name = "sample_data" + + def get_package_data(self): + return { + 'matplotlib': + [ + 'mpl-data/sample_data/*.*', + 'mpl-data/sample_data/axes_grid/*.*', + ]} + + +class Toolkits(OptionalPackage): + name = "toolkits" + + def get_packages(self): + return [ + 'mpl_toolkits', + 'mpl_toolkits.mplot3d', + 'mpl_toolkits.axes_grid', + 'mpl_toolkits.axes_grid1', + 'mpl_toolkits.axisartist', + ] + + def get_namespace_packages(self): + return ['mpl_toolkits'] + + +class Tests(OptionalPackage): + name = "tests" + pytest_min_version = '3.6' + default_config = False + + def check(self): + super(Tests, self).check() + + msgs = [] + msg_template = ('{package} is required to run the Matplotlib test ' + 'suite. Please install it with pip or your preferred ' + 'tool to run the test suite') + + bad_pytest = msg_template.format( + package='pytest %s or later' % self.pytest_min_version + ) + try: + import pytest + if is_min_version(pytest.__version__, self.pytest_min_version): + msgs += ['using pytest version %s' % pytest.__version__] + else: + msgs += [bad_pytest] + except ImportError: + msgs += [bad_pytest] + + if PY3min: + msgs += ['using unittest.mock'] + else: + try: + import mock + msgs += ['using mock %s' % mock.__version__] + except ImportError: + msgs += [msg_template.format(package='mock')] + + return ' / '.join(msgs) + + def get_packages(self): + return [ + 'matplotlib.tests', + 'matplotlib.sphinxext.tests', + ] + + def get_package_data(self): + baseline_images = [ + 'tests/baseline_images/%s/*' % x + for x in os.listdir('lib/matplotlib/tests/baseline_images')] + + return { + 'matplotlib': + baseline_images + + [ + 'tests/cmr10.pfb', + 'tests/mpltest.ttf', + 'tests/test_rcparams.rc', + 'tests/test_utf32_be_rcparams.rc', + 'sphinxext/tests/tinypages/*.rst', + 'sphinxext/tests/tinypages/*.py', + 'sphinxext/tests/tinypages/_static/*', + ]} + + +class Toolkits_Tests(Tests): + name = "toolkits_tests" + + def check_requirements(self): + conf = self.get_config() + toolkits_conf = Toolkits.get_config() + tests_conf = Tests.get_config() + + if conf is True: + Tests.force = True + Toolkits.force = True + elif conf == "auto" and not (toolkits_conf and tests_conf): + # Only auto-install if both toolkits and tests are set + # to be installed + raise CheckFailed("toolkits_tests needs 'toolkits' and 'tests'") + return "" + + def get_packages(self): + return [ + 'mpl_toolkits.tests', + ] + + def get_package_data(self): + baseline_images = [ + 'tests/baseline_images/%s/*' % x + for x in os.listdir('lib/mpl_toolkits/tests/baseline_images')] + + return {'mpl_toolkits': baseline_images} + + def get_namespace_packages(self): + return ['mpl_toolkits'] + + +class DelayedExtension(Extension, object): + """ + A distutils Extension subclass where some of its members + may have delayed computation until reaching the build phase. + + This is so we can, for example, get the Numpy include dirs + after pip has installed Numpy for us if it wasn't already + on the system. + """ + def __init__(self, *args, **kwargs): + super(DelayedExtension, self).__init__(*args, **kwargs) + self._finalized = False + self._hooks = {} + + def add_hook(self, member, func): + """ + Add a hook to dynamically compute a member. + + Parameters + ---------- + member : string + The name of the member + + func : callable + The function to call to get dynamically-computed values + for the member. + """ + self._hooks[member] = func + + def finalize(self): + self._finalized = True + + class DelayedMember(property): + def __init__(self, name): + self._name = name + + def __get__(self, obj, objtype=None): + result = getattr(obj, '_' + self._name, []) + + if obj._finalized: + if self._name in obj._hooks: + result = obj._hooks[self._name]() + result + + return result + + def __set__(self, obj, value): + setattr(obj, '_' + self._name, value) + + include_dirs = DelayedMember('include_dirs') + + +class Numpy(SetupPackage): + name = "numpy" + + @staticmethod + def include_dirs_hook(): + if PY3min: + import builtins + if hasattr(builtins, '__NUMPY_SETUP__'): + del builtins.__NUMPY_SETUP__ + import imp + import numpy + imp.reload(numpy) + else: + import __builtin__ + if hasattr(__builtin__, '__NUMPY_SETUP__'): + del __builtin__.__NUMPY_SETUP__ + import numpy + reload(numpy) + + ext = Extension('test', []) + ext.include_dirs.append(numpy.get_include()) + if not has_include_file( + ext.include_dirs, os.path.join("numpy", "arrayobject.h")): + warnings.warn( + "The C headers for numpy could not be found. " + "You may need to install the development package") + + return [numpy.get_include()] + + def check(self): + min_version = extract_versions()['__version__numpy__'] + try: + import numpy + except ImportError: + return 'not found. pip may install it below.' + + if not is_min_version(numpy.__version__, min_version): + raise SystemExit( + "Requires numpy %s or later to build. (Found %s)" % + (min_version, numpy.__version__)) + + return 'version %s' % numpy.__version__ + + def add_flags(self, ext): + # Ensure that PY_ARRAY_UNIQUE_SYMBOL is uniquely defined for + # each extension + array_api_name = 'MPL_' + ext.name.replace('.', '_') + '_ARRAY_API' + + ext.define_macros.append(('PY_ARRAY_UNIQUE_SYMBOL', array_api_name)) + ext.add_hook('include_dirs', self.include_dirs_hook) + + ext.define_macros.append(('NPY_NO_DEPRECATED_API', + 'NPY_1_7_API_VERSION')) + + # Allow NumPy's printf format specifiers in C++. + ext.define_macros.append(('__STDC_FORMAT_MACROS', 1)) + + def get_setup_requires(self): + return ['numpy>=1.7.1'] + + def get_install_requires(self): + return ['numpy>=1.7.1'] + + +class LibAgg(SetupPackage): + name = 'libagg' + + def check(self): + self.__class__.found_external = True + try: + return self._check_for_pkg_config( + 'libagg', 'agg2/agg_basics.h', min_version='PATCH') + except CheckFailed as e: + self.__class__.found_external = False + return str(e) + ' Using local copy.' + + def add_flags(self, ext, add_sources=True): + if self.found_external: + pkg_config.setup_extension(ext, 'libagg') + else: + ext.include_dirs.insert(0, 'extern/agg24-svn/include') + if add_sources: + agg_sources = [ + 'agg_bezier_arc.cpp', + 'agg_curves.cpp', + 'agg_image_filters.cpp', + 'agg_trans_affine.cpp', + 'agg_vcgen_contour.cpp', + 'agg_vcgen_dash.cpp', + 'agg_vcgen_stroke.cpp', + 'agg_vpgen_segmentator.cpp' + ] + ext.sources.extend( + os.path.join('extern', 'agg24-svn', 'src', x) for x in agg_sources) + + +class FreeType(SetupPackage): + name = "freetype" + pkg_names = { + "apt-get": "libfreetype6-dev", + "yum": "freetype-devel", + "dnf": "freetype-devel", + "brew": "freetype", + "port": "freetype", + "windows_url": "http://gnuwin32.sourceforge.net/packages/freetype.htm" + } + + def check(self): + if options.get('local_freetype'): + return "Using local version for testing" + + if sys.platform == 'win32': + try: + check_include_file(get_include_dirs(), 'ft2build.h', 'freetype') + except CheckFailed: + check_include_file(get_include_dirs(), os.path.join('freetype2', 'ft2build.h'), 'freetype') + return 'Using unknown version found on system.' + + status, output = getstatusoutput("freetype-config --ftversion") + if status == 0: + version = output + else: + version = None + + # Early versions of freetype grep badly inside freetype-config, + # so catch those cases. (tested with 2.5.3). + if version is None or 'No such file or directory\ngrep:' in version: + version = self.version_from_header() + + # pkg_config returns the libtool version rather than the + # freetype version so we need to explicitly pass the version + # to _check_for_pkg_config + return self._check_for_pkg_config( + 'freetype2', 'ft2build.h', + min_version='2.3', version=version) + + def version_from_header(self): + version = 'unknown' + ext = self.get_extension() + if ext is None: + return version + # Return the first version found in the include dirs. + for include_dir in ext.include_dirs: + header_fname = os.path.join(include_dir, 'freetype.h') + if os.path.exists(header_fname): + major, minor, patch = 0, 0, 0 + with open(header_fname, 'r') as fh: + for line in fh: + if line.startswith('#define FREETYPE_'): + value = line.rsplit(' ', 1)[1].strip() + if 'MAJOR' in line: + major = value + elif 'MINOR' in line: + minor = value + else: + patch = value + return '.'.join([major, minor, patch]) + + def add_flags(self, ext): + if options.get('local_freetype'): + src_path = os.path.join( + 'build', 'freetype-{0}'.format(LOCAL_FREETYPE_VERSION)) + # Statically link to the locally-built freetype. + # This is certainly broken on Windows. + ext.include_dirs.insert(0, os.path.join(src_path, 'include')) + if sys.platform == 'win32': + libfreetype = 'libfreetype.lib' + else: + libfreetype = 'libfreetype.a' + ext.extra_objects.insert( + 0, os.path.join(src_path, 'objs', '.libs', libfreetype)) + ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'local')) + else: + pkg_config.setup_extension( + ext, 'freetype2', + default_include_dirs=[ + 'include/freetype2', 'freetype2', + 'lib/freetype2/include', + 'lib/freetype2/include/freetype2'], + default_library_dirs=[ + 'freetype2/lib'], + default_libraries=['freetype', 'z']) + ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'system')) + + def do_custom_build(self): + # We're using a system freetype + if not options.get('local_freetype'): + return + + src_path = os.path.join( + 'build', 'freetype-{0}'.format(LOCAL_FREETYPE_VERSION)) + + # We've already built freetype + if sys.platform == 'win32': + libfreetype = 'libfreetype.lib' + else: + libfreetype = 'libfreetype.a' + + # bailing because it is already built + if os.path.isfile(os.path.join( + src_path, 'objs', '.libs', libfreetype)): + return + + # do we need to download / load the source from cache? + if not os.path.exists(src_path): + if not os.path.exists('build'): + os.makedirs('build') + + url_fmts = [ + ('https://downloads.sourceforge.net/project/freetype' + '/freetype2/{version}/{tarball}'), + ('https://download.savannah.gnu.org/releases/freetype' + '/{tarball}') + ] + tarball = 'freetype-{0}.tar.gz'.format(LOCAL_FREETYPE_VERSION) + + target_urls = [ + url_fmt.format(version=LOCAL_FREETYPE_VERSION, + tarball=tarball) + for url_fmt in url_fmts] + + for tarball_url in target_urls: + try: + tar_contents = download_or_cache(tarball_url, + LOCAL_FREETYPE_HASH) + break + except Exception: + pass + else: + raise IOError("Failed to download FreeType. Please download " + "one of {target_urls} and extract it into " + "{src_path} at the top-level of the source " + "repository".format( + target_urls=target_urls, src_path=src_path)) + + print("Extracting {}".format(tarball)) + # just to be sure + tar_contents.seek(0) + with tarfile.open(tarball, mode="r:gz", + fileobj=tar_contents) as tgz: + tgz.extractall("build") + + print("Building freetype in {}".format(src_path)) + if sys.platform != 'win32': + # compilation on all other platforms than windows + cflags = 'CFLAGS="{0} -fPIC" '.format(os.environ.get('CFLAGS', '')) + + subprocess.check_call( + [cflags + './configure --with-zlib=no --with-bzip2=no ' + '--with-png=no --with-harfbuzz=no'], shell=True, cwd=src_path) + subprocess.check_call( + [cflags + 'make'], shell=True, cwd=src_path) + else: + # compilation on windows + FREETYPE_BUILD_CMD = """\ +call "%ProgramFiles%\\Microsoft SDKs\\Windows\\v7.0\\Bin\\SetEnv.Cmd" /Release /{xXX} /xp +call "{vcvarsall}" {xXX} +set MSBUILD=C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\MSBuild.exe +rd /S /Q %FREETYPE%\\objs +%MSBUILD% %FREETYPE%\\builds\\windows\\{vc20xx}\\freetype.sln /t:Clean;Build /p:Configuration="{config}";Platform={WinXX} +echo Build completed, moving result" +:: move to the "normal" path for the unix builds... +mkdir %FREETYPE%\\objs\\.libs +:: REMINDER: fix when changing the version +copy %FREETYPE%\\objs\\{vc20xx}\\{xXX}\\freetype261.lib %FREETYPE%\\objs\\.libs\\libfreetype.lib +if errorlevel 1 ( + rem This is a py27 version, which has a different location for the lib file :-/ + copy %FREETYPE%\\objs\\win32\\{vc20xx}\\freetype261.lib %FREETYPE%\\objs\\.libs\\libfreetype.lib +) +""" + from setup_external_compile import fixproj, prepare_build_cmd, VS2010, X64 + # Note: freetype has no build profile for 2014, so we don't bother... + vc = 'vc2010' if VS2010 else 'vc2008' + WinXX = 'x64' if X64 else 'Win32' + # This is only false for py2.7, even on py3.5... + if not VS2010: + fixproj(os.path.join(src_path, 'builds', 'windows', vc, 'freetype.sln'), WinXX) + fixproj(os.path.join(src_path, 'builds', 'windows', vc, 'freetype.vcproj'), WinXX) + + cmdfile = os.path.join("build", 'build_freetype.cmd') + with open(cmdfile, 'w') as cmd: + cmd.write(prepare_build_cmd(FREETYPE_BUILD_CMD, vc20xx=vc, WinXX=WinXX, + config='Release' if VS2010 else 'LIB Release')) + + os.environ['FREETYPE'] = src_path + subprocess.check_call([cmdfile], shell=True) + + +class FT2Font(SetupPackage): + name = 'ft2font' + + def get_extension(self): + sources = [ + 'src/ft2font.cpp', + 'src/ft2font_wrapper.cpp', + 'src/mplutils.cpp' + ] + ext = make_extension('matplotlib.ft2font', sources) + FreeType().add_flags(ext) + Numpy().add_flags(ext) + return ext + + +class Png(SetupPackage): + name = "png" + pkg_names = { + "apt-get": "libpng12-dev", + "yum": "libpng-devel", + "dnf": "libpng-devel", + "brew": "libpng", + "port": "libpng", + "windows_url": "http://gnuwin32.sourceforge.net/packages/libpng.htm" + } + + def check(self): + if sys.platform == 'win32': + check_include_file(get_include_dirs(), 'png.h', 'png') + return 'Using unknown version found on system.' + + status, output = getstatusoutput("libpng-config --version") + if status == 0: + version = output + else: + version = None + + try: + return self._check_for_pkg_config( + 'libpng', 'png.h', + min_version='1.2', version=version) + except CheckFailed as e: + if has_include_file(get_include_dirs(), 'png.h'): + return str(e) + ' Using unknown version found on system.' + raise + + def get_extension(self): + sources = [ + 'src/_png.cpp', + 'src/mplutils.cpp' + ] + ext = make_extension('matplotlib._png', sources) + pkg_config.setup_extension( + ext, 'libpng', default_libraries=['png', 'z'], + alt_exec='libpng-config --ldflags') + Numpy().add_flags(ext) + return ext + + +class Qhull(SetupPackage): + name = "qhull" + + def check(self): + self.__class__.found_external = True + try: + return self._check_for_pkg_config( + 'libqhull', 'libqhull/qhull_a.h', min_version='2015.2') + except CheckFailed as e: + self.__class__.found_pkgconfig = False + self.__class__.found_external = False + return str(e) + ' Using local copy.' + + def add_flags(self, ext): + if self.found_external: + pkg_config.setup_extension(ext, 'qhull', + default_libraries=['qhull']) + else: + ext.include_dirs.insert(0, 'extern') + ext.sources.extend(sorted(glob.glob('extern/libqhull/*.c'))) + if sysconfig.get_config_var('LIBM') == '-lm': + ext.libraries.extend('m') + + +class TTConv(SetupPackage): + name = "ttconv" + + def get_extension(self): + sources = [ + 'src/_ttconv.cpp', + 'extern/ttconv/pprdrv_tt.cpp', + 'extern/ttconv/pprdrv_tt2.cpp', + 'extern/ttconv/ttutil.cpp' + ] + ext = make_extension('matplotlib.ttconv', sources) + Numpy().add_flags(ext) + ext.include_dirs.insert(0, 'extern') + return ext + + +class Path(SetupPackage): + name = "path" + + def get_extension(self): + sources = [ + 'src/py_converters.cpp', + 'src/_path_wrapper.cpp' + ] + + ext = make_extension('matplotlib._path', sources) + Numpy().add_flags(ext) + LibAgg().add_flags(ext) + return ext + + +class Image(SetupPackage): + name = "image" + + def get_extension(self): + sources = [ + 'src/_image.cpp', + 'src/mplutils.cpp', + 'src/_image_wrapper.cpp', + 'src/py_converters.cpp' + ] + ext = make_extension('matplotlib._image', sources) + Numpy().add_flags(ext) + LibAgg().add_flags(ext) + + return ext + + +class Contour(SetupPackage): + name = "contour" + + def get_extension(self): + sources = [ + "src/_contour.cpp", + "src/_contour_wrapper.cpp", + ] + ext = make_extension('matplotlib._contour', sources) + Numpy().add_flags(ext) + return ext + + +class QhullWrap(SetupPackage): + name = "qhull_wrap" + + def get_extension(self): + sources = ['src/qhull_wrap.c'] + ext = make_extension('matplotlib._qhull', sources, + define_macros=[('MPL_DEVNULL', os.devnull)]) + Numpy().add_flags(ext) + Qhull().add_flags(ext) + return ext + + +class Tri(SetupPackage): + name = "tri" + + def get_extension(self): + sources = [ + "lib/matplotlib/tri/_tri.cpp", + "lib/matplotlib/tri/_tri_wrapper.cpp", + "src/mplutils.cpp" + ] + ext = make_extension('matplotlib._tri', sources) + Numpy().add_flags(ext) + return ext + + +class InstallRequires(SetupPackage): + name = "install_requires" + + def check(self): + not_available = [] + wrong_version = [] + inst_req = self.get_install_requires() + for pack_inf in inst_req: + pack_inf_disp = pack_inf.split('>=') + if 'dateutil' in pack_inf_disp[0]: + pack_inf_disp[0] = 'dateutil' + pack_name = pack_inf_disp[0] + try: + import_module(pack_name) + if pack_name != pack_inf_disp[-1]: + # This means that we have to check for the version + pack_ver = sys.modules[pack_name].__version__ + pack_ver = [int(ele) for ele in pack_ver.split('.')] + ver_cond = pack_inf_disp[1].split(',!=') + # Check for minimum version + if pack_ver < [int(ele) for ele in ver_cond[0].split('.')]: + if len(ver_cond[1:]) > 0: + wrong_version.append(pack_name + +" is not at least at version " + +ver_cond[0]+os.linesep + +"Please upgrade!"+os.linesep + +"WARNING: Version(s) " + +", ".join(ver_cond[1:]) + +" have issues and must be " + +"avoided.") + else: + wrong_version.append(pack_name + +" is not at least at version " + +ver_cond[0]+os.linesep + +"Please upgrade!") + continue + # Check for forbidden versions if any + for ver in ver_cond[1:]: + if pack_ver == [int(ele) for ele in ver.split('.')]: + wrong_version.append(pack_name+" is at version " + +ver+" which is not allowed."+os.linesep + +"Please use a version newer than "+ver_cond[0] + +" but different from "+", ".join(ver_cond[1:])) + break + except ImportError: + not_available.append(pack_name+" could not be found") + if not_available or wrong_version: + sp_mult = min(1, len(wrong_version)) + req_fail_msg = "ERROR: At least one third-party python package " + \ + "is missing or has the wrong version:" + os.linesep + req_fail_msg += (os.linesep.join(not_available) + + os.linesep*(2*sp_mult)) * min(1, len(not_available)) + req_fail_msg += (os.linesep*2).join(wrong_version) + print_message(req_fail_msg) + raise CheckFailed("missing or faulty third-party python packages") + return "all third-party python packages are present" + + def get_install_requires(self): + install_requires = [ + "cycler>=0.10", + "pyparsing>=2.0.1,!=2.0.4,!=2.1.2,!=2.1.6", + "python-dateutil>=2.1", + "pytz", + "six>=1.10", + "kiwisolver>=1.0.1", + ] + if sys.version_info < (3,): + install_requires += ["backports.functools_lru_cache"] + if sys.version_info < (3,) and os.name == "posix": + install_requires += ["subprocess32"] + return install_requires + + +class BackendAgg(OptionalBackendPackage): + name = "agg" + force = True + + def get_extension(self): + sources = [ + "src/mplutils.cpp", + "src/py_converters.cpp", + "src/_backend_agg.cpp", + "src/_backend_agg_wrapper.cpp" + ] + ext = make_extension('matplotlib.backends._backend_agg', sources) + Numpy().add_flags(ext) + LibAgg().add_flags(ext) + FreeType().add_flags(ext) + return ext + + +class BackendTkAgg(OptionalBackendPackage): + name = "tkagg" + force = True + + def check(self): + return "installing; run-time loading from Python Tcl / Tk" + + def runtime_check(self): + """ Checks whether TkAgg runtime dependencies are met + """ + pkg_name = 'tkinter' if PY3min else 'Tkinter' + try: + import_module(pkg_name) + except ImportError: + return False + return True + + def get_extension(self): + sources = [ + 'src/_tkagg.cpp' + ] + + ext = make_extension('matplotlib.backends._tkagg', sources) + self.add_flags(ext) + LibAgg().add_flags(ext, add_sources=False) + return ext + + def add_flags(self, ext): + ext.include_dirs.insert(0, 'src') + if sys.platform == 'win32': + # PSAPI library needed for finding Tcl / Tk at run time + ext.libraries.extend(['psapi']) + elif sys.platform.startswith('linux'): + ext.libraries.extend(['dl']) + + +class BackendGtk(OptionalBackendPackage): + name = "gtk" + + def check_requirements(self): + try: + import gtk + except ImportError: + raise CheckFailed("Requires pygtk") + except RuntimeError: + raise CheckFailed('pygtk present, but import failed.') + else: + version = (2, 2, 0) + if gtk.pygtk_version < version: + raise CheckFailed( + "Requires pygtk %d.%d.%d or later. " + "Found %d.%d.%d" % (version + gtk.pygtk_version)) + + ext = self.get_extension() + self.add_flags(ext) + check_include_file(ext.include_dirs, + os.path.join("gtk", "gtk.h"), + 'gtk') + check_include_file(ext.include_dirs, + os.path.join("pygtk", "pygtk.h"), + 'pygtk') + + return 'Gtk: %s pygtk: %s' % ( + ".".join(str(x) for x in gtk.gtk_version), + ".".join(str(x) for x in gtk.pygtk_version)) + + def get_package_data(self): + return {'matplotlib': ['mpl-data/*.glade']} + + def get_extension(self): + sources = [ + 'src/_backend_gdk.c' + ] + ext = make_extension('matplotlib.backends._backend_gdk', sources) + self.add_flags(ext) + Numpy().add_flags(ext) + return ext + + def add_flags(self, ext): + if sys.platform == 'win32': + def getoutput(s): + ret = os.popen(s).read().strip() + return ret + + if 'PKG_CONFIG_PATH' not in os.environ: + # If Gtk+ is installed, pkg-config is required to be installed + os.environ['PKG_CONFIG_PATH'] = 'C:\\GTK\\lib\\pkgconfig' + + # popen broken on my win32 platform so I can't use pkgconfig + ext.library_dirs.extend( + ['C:/GTK/bin', 'C:/GTK/lib']) + + ext.include_dirs.extend( + ['win32_static/include/pygtk-2.0', + 'C:/GTK/include', + 'C:/GTK/include/gobject', + 'C:/GTK/include/gext', + 'C:/GTK/include/glib', + 'C:/GTK/include/pango', + 'C:/GTK/include/atk', + 'C:/GTK/include/X11', + 'C:/GTK/include/cairo', + 'C:/GTK/include/gdk', + 'C:/GTK/include/gdk-pixbuf', + 'C:/GTK/include/gtk', + ]) + + pygtkIncludes = getoutput( + 'pkg-config --cflags-only-I pygtk-2.0').split() + gtkIncludes = getoutput( + 'pkg-config --cflags-only-I gtk+-2.0').split() + includes = pygtkIncludes + gtkIncludes + ext.include_dirs.extend([include[2:] for include in includes]) + + pygtkLinker = getoutput('pkg-config --libs pygtk-2.0').split() + gtkLinker = getoutput('pkg-config --libs gtk+-2.0').split() + linkerFlags = pygtkLinker + gtkLinker + + ext.libraries.extend( + [flag[2:] for flag in linkerFlags if flag.startswith('-l')]) + + ext.library_dirs.extend( + [flag[2:] for flag in linkerFlags if flag.startswith('-L')]) + + ext.extra_link_args.extend( + [flag for flag in linkerFlags if not + (flag.startswith('-l') or flag.startswith('-L'))]) + + # visual studio doesn't need the math library + if (sys.platform == 'win32' and + win32_compiler == 'msvc' and + 'm' in ext.libraries): + ext.libraries.remove('m') + + elif sys.platform != 'win32': + pkg_config.setup_extension(ext, 'pygtk-2.0') + pkg_config.setup_extension(ext, 'gtk+-2.0') + + +class BackendGtkAgg(BackendGtk): + name = "gtkagg" + + def get_package_data(self): + return {'matplotlib': ['mpl-data/*.glade']} + + def get_extension(self): + sources = [ + 'src/py_converters.cpp', + 'src/_gtkagg.cpp', + 'src/mplutils.cpp' + ] + ext = make_extension('matplotlib.backends._gtkagg', sources) + self.add_flags(ext) + LibAgg().add_flags(ext) + Numpy().add_flags(ext) + return ext + + +def backend_gtk3agg_internal_check(x): + try: + import gi + except ImportError: + return (False, "Requires pygobject to be installed.") + + try: + gi.require_version("Gtk", "3.0") + except ValueError: + return (False, "Requires gtk3 development files to be installed.") + except AttributeError: + return (False, "pygobject version too old.") + + try: + from gi.repository import Gtk, Gdk, GObject + except (ImportError, RuntimeError): + return (False, "Requires pygobject to be installed.") + + return (True, "version %s.%s.%s" % ( + Gtk.get_major_version(), + Gtk.get_micro_version(), + Gtk.get_minor_version())) + + +class BackendGtk3Agg(OptionalBackendPackage): + name = "gtk3agg" + + def check_requirements(self): + if 'TRAVIS' in os.environ: + raise CheckFailed("Can't build with Travis") + + # This check needs to be performed out-of-process, because + # importing gi and then importing regular old pygtk afterward + # segfaults the interpreter. + try: + p = multiprocessing.Pool() + except: + return "unknown (can not use multiprocessing to determine)" + try: + res = p.map_async(backend_gtk3agg_internal_check, [0]) + success, msg = res.get(timeout=10)[0] + except multiprocessing.TimeoutError: + p.terminate() + # No result returned. Probably hanging, terminate the process. + success = False + raise CheckFailed("Check timed out") + except: + p.close() + # Some other error. + success = False + msg = "Could not determine" + raise + else: + p.close() + finally: + p.join() + + if success: + return msg + else: + raise CheckFailed(msg) + + def get_package_data(self): + return {'matplotlib': ['mpl-data/*.glade']} + + +def backend_gtk3cairo_internal_check(x): + try: + import cairocffi + except ImportError: + try: + import cairo + except ImportError: + return (False, "Requires cairocffi or pycairo to be installed.") + + try: + import gi + except ImportError: + return (False, "Requires pygobject to be installed.") + + try: + gi.require_version("Gtk", "3.0") + except ValueError: + return (False, "Requires gtk3 development files to be installed.") + except AttributeError: + return (False, "pygobject version too old.") + + try: + from gi.repository import Gtk, Gdk, GObject + except (RuntimeError, ImportError): + return (False, "Requires pygobject to be installed.") + + return (True, "version %s.%s.%s" % ( + Gtk.get_major_version(), + Gtk.get_micro_version(), + Gtk.get_minor_version())) + + +class BackendGtk3Cairo(OptionalBackendPackage): + name = "gtk3cairo" + + def check_requirements(self): + if 'TRAVIS' in os.environ: + raise CheckFailed("Can't build with Travis") + + # This check needs to be performed out-of-process, because + # importing gi and then importing regular old pygtk afterward + # segfaults the interpreter. + try: + p = multiprocessing.Pool() + except: + return "unknown (can not use multiprocessing to determine)" + try: + res = p.map_async(backend_gtk3cairo_internal_check, [0]) + success, msg = res.get(timeout=10)[0] + except multiprocessing.TimeoutError: + p.terminate() + # No result returned. Probably hanging, terminate the process. + success = False + raise CheckFailed("Check timed out") + except: + p.close() + success = False + raise + else: + p.close() + finally: + p.join() + + if success: + return msg + else: + raise CheckFailed(msg) + + def get_package_data(self): + return {'matplotlib': ['mpl-data/*.glade']} + + +class BackendWxAgg(OptionalBackendPackage): + name = "wxagg" + + def check_requirements(self): + wxversioninstalled = True + try: + import wxversion + except ImportError: + wxversioninstalled = False + + if wxversioninstalled: + try: + _wx_ensure_failed = wxversion.AlreadyImportedError + except AttributeError: + _wx_ensure_failed = wxversion.VersionError + + try: + wxversion.ensureMinimal('2.9') + except _wx_ensure_failed: + pass + + try: + import wx + backend_version = wx.VERSION_STRING + except ImportError: + raise CheckFailed("requires wxPython") + + if not is_min_version(backend_version, "2.9"): + raise CheckFailed( + "Requires wxPython 2.9, found %s" % backend_version) + + return "version %s" % backend_version + + +class BackendMacOSX(OptionalBackendPackage): + name = 'macosx' + + def check_requirements(self): + if sys.platform != 'darwin': + raise CheckFailed("Mac OS-X only") + + return 'darwin' + + def get_extension(self): + sources = [ + 'src/_macosx.m' + ] + + ext = make_extension('matplotlib.backends._macosx', sources) + ext.extra_link_args.extend(['-framework', 'Cocoa']) + return ext + + +class Windowing(OptionalBackendPackage): + """ + Builds the windowing extension. + """ + name = "windowing" + + def check_requirements(self): + if sys.platform != 'win32': + raise CheckFailed("Microsoft Windows only") + config = self.get_config() + if config is False: + raise CheckFailed("skipping due to configuration") + return "" + + def get_extension(self): + sources = [ + "src/_windowing.cpp" + ] + ext = make_extension('matplotlib._windowing', sources) + ext.include_dirs.extend(['C:/include']) + ext.libraries.extend(['user32']) + ext.library_dirs.extend(['C:/lib']) + ext.extra_link_args.append("-mwindows") + return ext + + +class BackendQtBase(OptionalBackendPackage): + + def convert_qt_version(self, version): + version = '%x' % version + temp = [] + while len(version) > 0: + version, chunk = version[:-2], version[-2:] + temp.insert(0, str(int(chunk, 16))) + return '.'.join(temp) + + def check_requirements(self): + ''' + If PyQt4/PyQt5 is already imported, importing PyQt5/PyQt4 will fail + so we need to test in a subprocess (as for Gtk3). + ''' + try: + p = multiprocessing.Pool() + + except: + # Can't do multiprocessing, fall back to normal approach + # (this will fail if importing both PyQt4 and PyQt5). + try: + # Try in-process + msg = self.callback(self) + except RuntimeError: + raise CheckFailed( + "Could not import: are PyQt4 & PyQt5 both installed?") + + else: + # Multiprocessing OK + try: + res = p.map_async(self.callback, [self]) + msg = res.get(timeout=10)[0] + except multiprocessing.TimeoutError: + p.terminate() + # No result returned. Probably hanging, terminate the process. + raise CheckFailed("Check timed out") + except: + # Some other error. + p.close() + raise + else: + # Clean exit + p.close() + finally: + # Tidy up multiprocessing + p.join() + + return msg + + +def backend_pyside_internal_check(self): + try: + from PySide import __version__ + from PySide import QtCore + except ImportError: + raise CheckFailed("PySide not found") + else: + return ("Qt: %s, PySide: %s" % + (QtCore.__version__, __version__)) + + +def backend_pyqt4_internal_check(self): + try: + from PyQt4 import QtCore + except ImportError: + raise CheckFailed("PyQt4 not found") + + try: + qt_version = QtCore.QT_VERSION + pyqt_version_str = QtCore.PYQT_VERSION_STR + except AttributeError: + raise CheckFailed('PyQt4 not correctly imported') + else: + return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str)) + + +def backend_qt4_internal_check(self): + successes = [] + failures = [] + try: + successes.append(backend_pyside_internal_check(self)) + except CheckFailed as e: + failures.append(str(e)) + + try: + successes.append(backend_pyqt4_internal_check(self)) + except CheckFailed as e: + failures.append(str(e)) + + if len(successes) == 0: + raise CheckFailed('; '.join(failures)) + return '; '.join(successes + failures) + + +class BackendQt4(BackendQtBase): + name = "qt4agg" + + def __init__(self, *args, **kwargs): + BackendQtBase.__init__(self, *args, **kwargs) + self.callback = backend_qt4_internal_check + +def backend_pyside2_internal_check(self): + try: + from PySide2 import __version__ + from PySide2 import QtCore + except ImportError: + raise CheckFailed("PySide2 not found") + else: + return ("Qt: %s, PySide2: %s" % + (QtCore.__version__, __version__)) + +def backend_pyqt5_internal_check(self): + try: + from PyQt5 import QtCore + except ImportError: + raise CheckFailed("PyQt5 not found") + + try: + qt_version = QtCore.QT_VERSION + pyqt_version_str = QtCore.PYQT_VERSION_STR + except AttributeError: + raise CheckFailed('PyQt5 not correctly imported') + else: + return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str)) + +def backend_qt5_internal_check(self): + successes = [] + failures = [] + try: + successes.append(backend_pyside2_internal_check(self)) + except CheckFailed as e: + failures.append(str(e)) + + try: + successes.append(backend_pyqt5_internal_check(self)) + except CheckFailed as e: + failures.append(str(e)) + + if len(successes) == 0: + raise CheckFailed('; '.join(failures)) + return '; '.join(successes + failures) + +class BackendQt5(BackendQtBase): + name = "qt5agg" + + def __init__(self, *args, **kwargs): + BackendQtBase.__init__(self, *args, **kwargs) + self.callback = backend_qt5_internal_check + + +class BackendCairo(OptionalBackendPackage): + name = "cairo" + + def check_requirements(self): + try: + import cairocffi + except ImportError: + try: + import cairo + except ImportError: + raise CheckFailed("cairocffi or pycairo not found") + else: + return "pycairo version %s" % cairo.version + else: + return "cairocffi version %s" % cairocffi.version + + +class DviPng(SetupPackage): + name = "dvipng" + optional = True + + def check(self): + try: + output = check_output('dvipng -version', shell=True, + stderr=subprocess.STDOUT) + return "version %s" % output.splitlines()[1].decode().split()[-1] + except (IndexError, ValueError, subprocess.CalledProcessError): + raise CheckFailed() + + +class Ghostscript(SetupPackage): + name = "ghostscript" + optional = True + + def check(self): + if sys.platform == 'win32': + # mgs is the name in miktex + gs_execs = ['gswin32c', 'gswin64c', 'mgs', 'gs'] + else: + gs_execs = ['gs'] + for gs_exec in gs_execs: + try: + command = gs_exec + ' --version' + output = check_output(command, shell=True, + stderr=subprocess.STDOUT) + return "version %s" % output.decode()[:-1] + except (IndexError, ValueError, subprocess.CalledProcessError): + pass + + raise CheckFailed() + + +class LaTeX(SetupPackage): + name = "latex" + optional = True + + def check(self): + try: + output = check_output('latex -version', shell=True, + stderr=subprocess.STDOUT) + line = output.splitlines()[0].decode() + pattern = r'(3\.1\d+)|(MiKTeX \d+.\d+)' + match = re.search(pattern, line) + return "version %s" % match.group(0) + except (IndexError, ValueError, AttributeError, subprocess.CalledProcessError): + raise CheckFailed() + + +class PdfToPs(SetupPackage): + name = "pdftops" + optional = True + + def check(self): + try: + output = check_output('pdftops -v', shell=True, + stderr=subprocess.STDOUT) + for line in output.splitlines(): + line = line.decode() + if 'version' in line: + return "version %s" % line.split()[2] + except (IndexError, ValueError, subprocess.CalledProcessError): + pass + + raise CheckFailed() + + +class OptionalPackageData(OptionalPackage): + config_category = "package_data" + + +class Dlls(OptionalPackageData): + """ + On Windows, this packages any DLL files that can be found in the + lib/matplotlib/* directories. + """ + name = "dlls" + + def check_requirements(self): + if sys.platform != 'win32': + raise CheckFailed("Microsoft Windows only") + + def get_package_data(self): + return {'': ['*.dll']} + + @classmethod + def get_config(cls): + """ + Look at `setup.cfg` and return one of ["auto", True, False] indicating + if the package is at default state ("auto"), forced by the user (True) + or opted-out (False). + """ + try: + return config.getboolean(cls.config_category, cls.name) + except: + return False # <-- default diff --git a/libraries/python2-matplotlib/slack-desc b/libraries/python2-matplotlib/slack-desc new file mode 100644 index 0000000000..c59df0596a --- /dev/null +++ b/libraries/python2-matplotlib/slack-desc @@ -0,0 +1,19 @@ +# HOW TO EDIT THIS FILE: +# The "handy ruler" below makes it easier to edit a package description. +# Line up the first '|' above the ':' following the base package name, and +# the '|' on the right side marks the last column you can put a character in. +# You must make exactly 11 lines for the formatting to be correct. It's also +# customary to leave one space after the ':' except on otherwise blank lines. + + |-----handy-ruler------------------------------------------------------| +python2-matplotlib: python2-matplotlib (a Python 2D plotting library) +python2-matplotlib: +python2-matplotlib: Matplotlib is a plotting library for Python which uses +python2-matplotlib: syntax similar to MATLAB. Matplotlib produces publication +python2-matplotlib: quality figures in a variety of hardcopy formats and +python2-matplotlib: interactive environments across platforms. +python2-matplotlib: +python2-matplotlib: Homepage: http://matplotlib.sourceforge.net/ +python2-matplotlib: +python2-matplotlib: Version 2.2.5 is the last available for python2. +python2-matplotlib: