From 81c8b9e215f727ea0e2123c500e43e827cbc3059 Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Tue, 23 Aug 2016 13:53:43 -0700 Subject: [PATCH 01/16] CI: add appveyor script and files for wheels When we release, we will want to build the Windows wheels as well. --- appveyor.yml | 182 +++++++++++++++++++++++++++++ ci/appveyor/vcvars64.bat | 1 + ci/conda_recipe/README.md | 3 + ci/conda_recipe/bld.bat | 16 +++ ci/conda_recipe/build.sh | 37 ++++++ ci/conda_recipe/cfg_qt4agg.patch | 13 +++ ci/conda_recipe/condaversion.patch | 15 +++ ci/conda_recipe/meta.yaml | 74 ++++++++++++ ci/conda_recipe/osx-tk.patch | 60 ++++++++++ ci/conda_recipe/rctmp_pyside.patch | 19 +++ ci/conda_recipe/run_test.py | 29 +++++ ci/travis/setup.cfg | 2 + 12 files changed, 451 insertions(+) create mode 100644 appveyor.yml create mode 100644 ci/appveyor/vcvars64.bat create mode 100644 ci/conda_recipe/README.md create mode 100644 ci/conda_recipe/bld.bat create mode 100644 ci/conda_recipe/build.sh create mode 100644 ci/conda_recipe/cfg_qt4agg.patch create mode 100644 ci/conda_recipe/condaversion.patch create mode 100644 ci/conda_recipe/meta.yaml create mode 100644 ci/conda_recipe/osx-tk.patch create mode 100644 ci/conda_recipe/rctmp_pyside.patch create mode 100644 ci/conda_recipe/run_test.py create mode 100644 ci/travis/setup.cfg diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000000..a2c6b3f3b6db --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,182 @@ +# With infos from +# http://tjelvarolsson.com/blog/how-to-continuously-test-your-python-code-on-windows-using-appveyor/ +# https://packaging.python.org/en/latest/appveyor/ +# https://github.com/rmcgibbo/python-appveyor-conda-example + +# Backslashes in quotes need to be escaped: \ -> "\\" + +environment: + + global: + # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the + # /E:ON and /V:ON options are not enabled in the batch script intepreter + # See: http://stackoverflow.com/a/13751649/163740 + CMD_IN_ENV: "cmd /E:ON /V:ON /C obvci_appveyor_python_build_env.cmd" + # Workaround for https://github.com/conda/conda-build/issues/636 + PYTHONIOENCODING: "UTF-8" + TEST_ARGS: --no-pep8 + PYTEST_ARGS: -ra --timeout=300 --durations=25 #--cov-report= --cov=lib #-n %NUMBER_OF_PROCESSORS% + USE_PYTEST: no + #PYTHONHASHSEED: 0 # Workaround for pytest-xdist flaky colletion order + # # https://github.com/pytest-dev/pytest/issues/920 + # # https://github.com/pytest-dev/pytest/issues/1075 + + matrix: + # for testing purpose: numpy 1.8 on py2.7, for the rest use 1.10/latest + # theoretically the CONDA_INSTALL_LOCN could be only two: one for 32bit, + # one for 64bit because we construct envs anyway. But using one for the + # right python version is hopefully making it fast due to package caching. + - TARGET_ARCH: "x64" + CONDA_PY: "27" + CONDA_NPY: "18" + PYTHON_VERSION: "2.7" + TEST_ALL: "no" + CONDA_INSTALL_LOCN: "C:\\Miniconda-x64" + - TARGET_ARCH: "x64" + CONDA_PY: "34" + CONDA_NPY: "110" + PYTHON_VERSION: "3.4" + TEST_ALL: "no" + CONDA_INSTALL_LOCN: "C:\\Miniconda3-x64" + - TARGET_ARCH: "x64" + CONDA_PY: "35" + CONDA_NPY: "110" + PYTHON_VERSION: "3.5" + TEST_ALL: "no" + CONDA_INSTALL_LOCN: "C:\\Miniconda35-x64" + - TARGET_ARCH: "x86" + CONDA_PY: "27" + CONDA_NPY: "18" + PYTHON_VERSION: "2.7" + # this variable influence pdf/svg and most importantly the latex related tests + # which triples the runtime of the tests (7-8min vs 30min). + # pick the one which seems to make the most problems and run it last, so that + # the rest of the tests can give feedback earlier + TEST_ALL: "yes" + CONDA_INSTALL_LOCN: "C:\\Miniconda" + +# We always use a 64-bit machine, but can build x86 distributions +# with the PYTHON_ARCH variable (which is used by CMD_IN_ENV). +platform: + - x64 + +# all our python builds have to happen in tests_script... +build: false + +init: + - cmd: "ECHO %PYTHON_VERSION% PYTEST=%USE_PYTEST% %CONDA_INSTALL_LOCN%" + +install: + - cmd: set PATH=%CONDA_INSTALL_LOCN%;%CONDA_INSTALL_LOCN%\scripts;%PATH%; + - cmd: set PYTHONUNBUFFERED=1 + # for obvci_appveyor_python_build_env.cmd + - cmd: conda install -c pelson/channel/development --yes --quiet obvious-ci + # for msinttypes and newer stuff + - cmd: conda config --add channels conda-forge + - cmd: conda config --set show_channel_urls yes + - cmd: conda config --set always_yes true + # For building conda packages + - cmd: conda install --yes conda-build jinja2 anaconda-client + # this is now the downloaded conda... + - conda info -a + + # Fix the appveyor build environment to work with conda build + # workaround for missing vcvars64.bat in py34 64bit + - cmd: copy ci\appveyor\vcvars64.bat "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64" + + # For building, use a new environment which only includes the requirements for mpl + # same things as the requirements in ci/conda_recipe/meta.yaml + # if conda-forge gets a new pyqt, it might be nice to install it as well to have more backends + # https://github.com/conda-forge/conda-forge.github.io/issues/157#issuecomment-223536381 + - conda create -q -n test-environment python=%PYTHON_VERSION% + pip setuptools numpy python-dateutil freetype=2.6 msinttypes "tk=8.5" + pyparsing pytz tornado "libpng>=1.6.21,<1.7" "zlib=1.2" "cycler>=0.10" + nose mock sphinx + - activate test-environment + - cmd: echo %PYTHON_VERSION% %TARGET_ARCH% + - cmd: IF %PYTHON_VERSION% == 2.7 conda install -q functools32 + # pytest-cov>=2.3.1 due to https://github.com/pytest-dev/pytest-cov/issues/124 + - if x%USE_PYTEST% == xyes conda install -q pytest "pytest-cov>=2.3.1" pytest-timeout #pytest-xdist + + # Let the install prefer the static builds of the libs + - set LIBRARY_LIB=%CONDA_PREFIX%\Library\lib + - cmd: 'mkdir lib || cmd /c "exit /b 0"' + - copy /Y %LIBRARY_LIB%\zlibstatic.lib lib\z.lib + - copy /Y %LIBRARY_LIB%\libpng_static.lib lib\png.lib + # These z.lib / png.lib are not static versions but files which end up as + # dependencies to the dll file. This is fine for the conda build, but not here + # and for the wheels + - del %LIBRARY_LIB%\png.lib + - del %LIBRARY_LIB%\z.lib + - set MPLBASEDIRLIST=%CONDA_PREFIX%\Library\;. + # enables the local freetype build + - copy ci\travis\setup.cfg . + # Show the installed packages + versions + - conda list + +test_script: + # Now build the thing.. + - '%CMD_IN_ENV% python setup.py develop' + # these should show no z, png, or freetype dll... + - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" + #- cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\_png*.pyd' + #- cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd' + - cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd | findstr freetype.*.dll && exit /b 1 || exit /b 0' + - cmd: '"%DUMPBIN%" /DEPENDENTS lib\\matplotlib\\_png*.pyd | findstr z.*.dll && exit /b 1 || exit /b 0' + - cmd: '"%DUMPBIN%" /DEPENDENTS lib\\matplotlib\\_png*.pyd | findstr png.*.dll && exit /b 1 || exit /b 0' + + # this are optional dependencies so that we don't skip so many tests... + - cmd: if x%TEST_ALL% == xyes; conda install -q pillow miktex inkscape + # missing packages on conda-forge for ffmpeg avconv mencoder imagemagick + - cmd: if x%TEST_ALL% == xyes; conda install -q -c menpo ffmpeg # a repackaged version + # This install sometimes failed randomly :-( + #- cmd: choco install imagemagick + + # Test import of tkagg backend + - python -c "import matplotlib as m; m.use('tkagg'); import matplotlib.pyplot as plt; print(plt.get_backend())" + # tests + - if x%USE_PYTEST% == xyes echo The following args are passed to pytest %PYTEST_ARGS% + - if x%USE_PYTEST% == xyes py.test %PYTEST_ARGS% %TEST_ARGS% + - if x%USE_PYTEST% == xno python tests.py %TEST_ARGS% + # Generate a html for visual tests + - python visual_tests.py + +after_test: + # After the tests were a success, build packages (wheels and conda) + + # Build the wheel with the static libs + # Hide the output, the copied files really clutter the build log... + - cmd: '%CMD_IN_ENV% python setup.py bdist_wheel > NUL:' + + # And now the conda build after a cleanup... + # cleanup build files so that they don't pollute the conda build but keep the wheel in dist... + - cmd: git clean -d -x -f -e dist/ + # cleanup the environment so that the test-environment does not leak into the conda build... + - cmd: set MPLBASEDIRLIST= + - cmd: set LIBRARY_LIB= + - cmd: deactivate + - cmd: path + - cmd: where python + - cmd: '%CMD_IN_ENV% conda config --get channels' + - cmd: '%CMD_IN_ENV% conda build -q .\ci\conda_recipe' + + # Move the conda package into the dist directory, to register it + # as an "artifact" for Appveyor. + - cmd: 'copy /Y %CONDA_INSTALL_LOCN%\conda-bld\win-32\*.bz2 dist || cmd /c "exit /b 0"' + - cmd: 'copy /Y %CONDA_INSTALL_LOCN%\conda-bld\win-64\*.bz2 dist || cmd /c "exit /b 0"' + - cmd: dir dist\ + - cmd: echo finished... + +artifacts: + - path: dist\* + name: packages + + - path: result_images\* + name: result_images + type: zip + +on_failure: + - python visual_tests.py + - echo zipping images after a failure... + - 7z a result_images.zip result_images\ |grep -v "Compressing" + - appveyor PushArtifact result_images.zip diff --git a/ci/appveyor/vcvars64.bat b/ci/appveyor/vcvars64.bat new file mode 100644 index 000000000000..c4659becc3ae --- /dev/null +++ b/ci/appveyor/vcvars64.bat @@ -0,0 +1 @@ +CALL "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 \ No newline at end of file diff --git a/ci/conda_recipe/README.md b/ci/conda_recipe/README.md new file mode 100644 index 000000000000..7819c9f0c766 --- /dev/null +++ b/ci/conda_recipe/README.md @@ -0,0 +1,3 @@ +# conda package + +Up to now, this is mainly used to build a test conda package on windows on appveyor. \ No newline at end of file diff --git a/ci/conda_recipe/bld.bat b/ci/conda_recipe/bld.bat new file mode 100644 index 000000000000..a7810d418d2f --- /dev/null +++ b/ci/conda_recipe/bld.bat @@ -0,0 +1,16 @@ +set LIBPATH=%LIBRARY_LIB%; +set INCLUDE=%INCLUDE%;%PREFIX%\Library\include\freetype2 + +ECHO [directories] > setup.cfg +ECHO basedirlist = %LIBRARY_PREFIX% >> setup.cfg +ECHO [packages] >> setup.cfg +ECHO tests = False >> setup.cfg +ECHO sample_data = False >> setup.cfg +ECHO toolkits_tests = False >> setup.cfg + +@rem workaround for https://github.com/matplotlib/matplotlib/issues/6460 +@rem see also https://github.com/conda-forge/libpng-feedstock/pull/4 +copy /y %LIBRARY_LIB%\libpng16.lib %LIBRARY_LIB%\png.lib + +%PYTHON% setup.py install --single-version-externally-managed --record=record.txt +if errorlevel 1 exit 1 diff --git a/ci/conda_recipe/build.sh b/ci/conda_recipe/build.sh new file mode 100644 index 000000000000..c2967acb98cf --- /dev/null +++ b/ci/conda_recipe/build.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +if [ `uname` == Linux ]; then + pushd $PREFIX/lib + ln -s libtcl8.5.so libtcl.so + ln -s libtk8.5.so libtk.so + popd +fi + +cat < setup.cfg +[directories] +basedirlist = $PREFIX + +[packages] +tests = False +toolkit_tests = False +sample_data = False + +EOF + +# The macosx backend isn't building with conda at this stage. +if [ `uname` == Darwin ]; then +cat << EOF >> setup.cfg + +[gui_support] +tkagg = true +macosx = false + +EOF +fi + +cat setup.cfg +sed -i.bak "s|/usr/local|$PREFIX|" setupext.py + + +$PYTHON setup.py install --single-version-externally-managed --record=record.txt + diff --git a/ci/conda_recipe/cfg_qt4agg.patch b/ci/conda_recipe/cfg_qt4agg.patch new file mode 100644 index 000000000000..16e6fc6c3934 --- /dev/null +++ b/ci/conda_recipe/cfg_qt4agg.patch @@ -0,0 +1,13 @@ +diff --git setup.cfg.template setup.cfg.template +index cae6f67..fd11c79 100644 +--- setup.cfg.template ++++ setup.cfg.template +@@ -88,7 +88,7 @@ + # if you have disabled the relevent extension modules. Agg will be used + # by default. + # +-#backend = Agg ++backend = Qt4Agg + # + + [package_data] diff --git a/ci/conda_recipe/condaversion.patch b/ci/conda_recipe/condaversion.patch new file mode 100644 index 000000000000..915fda3bcc23 --- /dev/null +++ b/ci/conda_recipe/condaversion.patch @@ -0,0 +1,15 @@ +diff --git setup.py setup.py +index 8af8b6d..4e4f9d2 100644 +--- setup.py ++++ setup.py +@@ -57,6 +57,9 @@ + import versioneer + __version__ = versioneer.get_version() + ++# For conda builds... ++with open("__conda_version__.txt", "w") as f: ++ f.write(__version__) + + # These are the packages in the order we want to display them. This + # list may contain strings to create section headers for the display. + \ No newline at end of file diff --git a/ci/conda_recipe/meta.yaml b/ci/conda_recipe/meta.yaml new file mode 100644 index 000000000000..5f992efff571 --- /dev/null +++ b/ci/conda_recipe/meta.yaml @@ -0,0 +1,74 @@ +# Full credit goes to https://github.com/conda/conda-recipes for providing this recipe. +# It has been subsequently adapted for automated building with conda-forge and matplotlib. + +package: + name: matplotlib + version: 1.9.9 + +source: + path: ../../ + + patches: + # A patch to make Qt4Agg the default backend. + - cfg_qt4agg.patch # [linux] + # Patches the matplotlibrc template to default to Qt4. + - rctmp_pyside.patch # [not osx] + # dynamic version from git + # we can't use condas usual dynamic versions as setup.py uses + # multiprocessing during the configure stage and this seems to confuse conda-build. + # https://github.com/conda/conda-build/issues/1061 + - condaversion.patch + + +requirements: + build: + - python + - setuptools + - pkg-config # [not win] + - numpy x.x + - python-dateutil + - freetype 2.6* + - msinttypes # [win] + - cycler >=0.10 + - nose + - pyparsing + - pytz +# - py2cairo # [linux and py2k] + - tornado + - libpng >=1.6.21,<1.7 + - zlib 1.2* # [win] + - pyqt # [not osx] + - tk 8.5* # [linux] + - functools32 # [py2k] + + run: + - python + - numpy x.x + - cycler >=0.10 + - python-dateutil + - freetype 2.6* + - pytz + - pyparsing +# - py2cairo # [linux and py2k] + - libpng >=1.6.21,<1.7 + - pyqt # [not osx] + - tk 8.5* # [linux and win] + - functools32 # [py2k] + +test: + imports: + - matplotlib + - matplotlib.pyplot + +about: + home: http://matplotlib.org/ + license: PSF-based (http://matplotlib.org/users/license.html) + summary: Publication quality figures in Python + +extra: + recipe-maintainers: + - janschulz # only in the mpl repository + - mdboom # rest form conda-forge + - ocefpaf + - pelson + - tacaswell diff --git a/ci/conda_recipe/osx-tk.patch b/ci/conda_recipe/osx-tk.patch new file mode 100644 index 000000000000..1411225550e9 --- /dev/null +++ b/ci/conda_recipe/osx-tk.patch @@ -0,0 +1,60 @@ +diff --git setupext.py setupext.py +index ddf2ca1..b9e0729 100755 +--- setupext.py ++++ setupext.py +@@ -1659,52 +1659,11 @@ class BackendTkAgg(OptionalBackendPackage): + ext.library_dirs.extend([os.path.join(sys.prefix, 'dlls')]) + + elif sys.platform == 'darwin': +- # this config section lifted directly from Imaging - thanks to +- # the effbot! +- +- # First test for a MacOSX/darwin framework install + from os.path import join, exists +- framework_dirs = [ +- join(os.getenv('HOME'), '/Library/Frameworks'), +- '/Library/Frameworks', +- '/System/Library/Frameworks/', +- ] + +- # Find the directory that contains the Tcl.framework and +- # Tk.framework bundles. +- tk_framework_found = 0 +- for F in framework_dirs: +- # both Tcl.framework and Tk.framework should be present +- for fw in 'Tcl', 'Tk': +- if not exists(join(F, fw + '.framework')): +- break +- else: +- # ok, F is now directory with both frameworks. Continure +- # building +- tk_framework_found = 1 +- break +- if tk_framework_found: +- # For 8.4a2, we must add -I options that point inside +- # the Tcl and Tk frameworks. In later release we +- # should hopefully be able to pass the -F option to +- # gcc, which specifies a framework lookup path. +- +- tk_include_dirs = [ +- join(F, fw + '.framework', H) +- for fw in ('Tcl', 'Tk') +- for H in ('Headers', 'Versions/Current/PrivateHeaders') +- ] +- +- # For 8.4a2, the X11 headers are not included. Rather +- # than include a complicated search, this is a +- # hard-coded path. It could bail out if X11 libs are +- # not found... +- +- # tk_include_dirs.append('/usr/X11R6/include') +- frameworks = ['-framework', 'Tcl', '-framework', 'Tk'] +- ext.include_dirs.extend(tk_include_dirs) +- ext.extra_link_args.extend(frameworks) +- ext.extra_compile_args.extend(frameworks) ++ ext.include_dirs.append(join(sys.prefix, 'include')) ++ ext.libraries.extend(['tk8.5', 'tcl8.5']) ++ ext.library_dirs.append(join(sys.prefix, 'lib')) + + # you're still here? ok we'll try it this way... + else: diff --git a/ci/conda_recipe/rctmp_pyside.patch b/ci/conda_recipe/rctmp_pyside.patch new file mode 100644 index 000000000000..906803575a90 --- /dev/null +++ b/ci/conda_recipe/rctmp_pyside.patch @@ -0,0 +1,19 @@ +diff --git matplotlibrc.template matplotlibrc.template +index fdbbf26..6902fe9 100644 +--- matplotlibrc.template ++++ matplotlibrc.template +@@ -35,12 +35,12 @@ + # You can also deploy your own backend outside of matplotlib by + # referring to the module name (which must be in the PYTHONPATH) as + # 'module://my_backend'. +-backend : $TEMPLATE_BACKEND ++backend : Qt4Agg + + # If you are using the Qt4Agg backend, you can choose here + # to use the PyQt4 bindings or the newer PySide bindings to + # the underlying Qt4 toolkit. +-#backend.qt4 : PyQt4 # PyQt4 | PySide ++backend.qt4 : PyQt4 # PyQt4 | PySide + + # Note that this can be overridden by the environment variable + # QT_API used by Enthought Tool Suite (ETS); valid values are diff --git a/ci/conda_recipe/run_test.py b/ci/conda_recipe/run_test.py new file mode 100644 index 000000000000..fba57d981e5d --- /dev/null +++ b/ci/conda_recipe/run_test.py @@ -0,0 +1,29 @@ +import os +import platform +import sys + +import matplotlib +import matplotlib.pyplot +import matplotlib._cntr +import matplotlib._delaunay +import matplotlib._image +import matplotlib._path +import matplotlib._png +import matplotlib._tri +import matplotlib.backends._backend_agg +import matplotlib.ft2font +import matplotlib.ttconv +if platform.system() not in ['Windows']: + import matplotlib.backends._tkagg + +import pylab +import mpl_toolkits + +if int(os.getenv('GUI_TEST', 0)): + assert matplotlib.rcParams['backend.qt4'] == 'PyQt4' + + import matplotlib.pyplot as plt + plt.ioff() + plt.title('If this window displays, success: CLOSE TO CONTINUE TESTS') + plt.plot([1,2,5,9]) + plt.show() diff --git a/ci/travis/setup.cfg b/ci/travis/setup.cfg new file mode 100644 index 000000000000..61cdc102a0f8 --- /dev/null +++ b/ci/travis/setup.cfg @@ -0,0 +1,2 @@ +[test] +local_freetype=True \ No newline at end of file From 6f9f62df74135860ebc60c79d723867a14088391 Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Tue, 23 Aug 2016 16:01:05 -0700 Subject: [PATCH 02/16] BF: backport visual_tests.py file --- visual_tests.py | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 visual_tests.py diff --git a/visual_tests.py b/visual_tests.py new file mode 100644 index 000000000000..f395b9d21d0b --- /dev/null +++ b/visual_tests.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# +# This builds a html page of all images from the image comparison tests +# and opens that page in the browser. +# +# $ python visual_tests.py +# + +import os +import time +import six + +from collections import defaultdict + +def run(): + # Build a website for visual comparison + image_dir = "result_images" + # build the website + _html = "" + _html += """ + \n""" + _subdirs = [name for name in os.listdir(image_dir) if os.path.isdir(os.path.join(image_dir, name))] + # loop over all pictures + _row = '{0} {1}{2}{4}\n' + _failed = "" + _failed += "

Only Failed

" + _failed += "\n\n" + _has_failure = False + _body = "" + for subdir in _subdirs: + if subdir == "test_compare_images": + # these are the image which test the image comparison functions... + continue + pictures = defaultdict(dict) + for file in os.listdir(os.path.join(image_dir, subdir)): + if os.path.isdir(os.path.join(image_dir, subdir, file)): + continue + fn, fext = os.path.splitext(file) + if fext != ".png": + continue + # Always use / for URLs. + if "-failed-diff" in fn: + pictures[fn[:-12]]["f"] = "/".join((subdir, file)) + elif "-expected" in fn: + pictures[fn[:-9]]["e"] = "/".join((subdir, file)) + else: + pictures[fn]["c"] = "/".join((subdir, file)) + + _body += "

{0}

".format(subdir) + _body += "
nameactualexpecteddiff
\n\n" + for name, test in six.iteritems(pictures): + if test.get("f", None): + # a real failure in the image generation, resulting in different images + _has_failure = True + s = "(failed)" + failed = 'diff'.format(test.get("f", "")) + current = ''.format(test.get("c", "")) + _failed += _row.format(name, "", current, test.get("e", ""), failed) + elif test.get("c", None) is None: + # A failure in the test, resulting in no current image + _has_failure = True + s = "(failed)" + failed = '--' + current = '(Failure in test, no image produced)' + _failed += _row.format(name, "", current, test.get("e", ""), failed) + else: + s = "(passed)" + failed = '--' + current = ''.format(test.get("c", "")) + _body += _row.format(name, "", current, test.get("e", ""), failed) + _body += "
nameactualexpecteddiff
\n" + _failed += "\n" + if _has_failure: + _html += _failed + _html += _body + _html += "\n" + index = os.path.join(image_dir, "index.html") + with open(index, "w") as f: + f.write(_html) + try: + import webbrowser + webbrowser.open(index) + except: + print("Open {0} in a browser for a visual comparison.".format(str(index))) + +if __name__ == '__main__': + run() From 5cc1faa90de3dca784e5702bf4e638bed15d3477 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 2 Dec 2015 23:19:38 +0100 Subject: [PATCH 03/16] BLD: Include conda dirs in basedir conda installs includes/libs in \Library, so adding this dir makes installing under conda much easier. --- setupext.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/setupext.py b/setupext.py index 5b84c0a4f121..b9c2bf8860e4 100755 --- a/setupext.py +++ b/setupext.py @@ -171,9 +171,15 @@ def get_base_dirs(): """ if options['basedirlist']: return options['basedirlist'] - + + win_bases = ['win32_static', ] + # on windows, we also add the \Library of the local interperter, as + # conda installs libs/includes there + if os.getenv('CONDA_DEFAULT_ENV'): + win_bases.append(os.path.join(os.getenv('CONDA_DEFAULT_ENV'), "Library")) + basedir_map = { - 'win32': ['win32_static', ], + 'win32': win_bases, 'darwin': ['/usr/local/', '/usr', '/usr/X11', '/opt/X11', '/opt/local'], 'sunos5': [os.getenv('MPLIB_BASE') or '/usr/local', ], From bdb7196b0ddcb99ad13c73e04b1a8a8151753d52 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 2 Dec 2015 23:39:48 +0100 Subject: [PATCH 04/16] BLD: Add a way to influence basedirs with env vars --- setupext.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/setupext.py b/setupext.py index b9c2bf8860e4..a59be00c85ab 100755 --- a/setupext.py +++ b/setupext.py @@ -172,9 +172,12 @@ def get_base_dirs(): if options['basedirlist']: return options['basedirlist'] + if os.environ.get('MPLBASEDIRLIST'): + return os.environ.get('MPLBASEDIRLIST').split(os.pathsep) + win_bases = ['win32_static', ] - # on windows, we also add the \Library of the local interperter, as - # conda installs libs/includes there + # on conda windows, we also add the \Library of the local interperter, + # as conda installs libs/includes there if os.getenv('CONDA_DEFAULT_ENV'): win_bases.append(os.path.join(os.getenv('CONDA_DEFAULT_ENV'), "Library")) @@ -194,8 +197,11 @@ 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()] - include_dirs.extend( - os.environ.get('CPLUS_INCLUDE_PATH', '').split(os.pathsep)) + 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 From be582488514c794eb59f53343d9d0e67f4da7f62 Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Wed, 24 Aug 2016 10:24:24 -0700 Subject: [PATCH 05/16] CI: enable extra checks of Windows DLLs From suggestion by Jan Schulz. --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a2c6b3f3b6db..bf16836d932e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -119,8 +119,8 @@ test_script: - '%CMD_IN_ENV% python setup.py develop' # these should show no z, png, or freetype dll... - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" - #- cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\_png*.pyd' - #- cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd' + - cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\_png*.pyd' + - cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd' - cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\ft2font*.pyd | findstr freetype.*.dll && exit /b 1 || exit /b 0' - cmd: '"%DUMPBIN%" /DEPENDENTS lib\\matplotlib\\_png*.pyd | findstr z.*.dll && exit /b 1 || exit /b 0' - cmd: '"%DUMPBIN%" /DEPENDENTS lib\\matplotlib\\_png*.pyd | findstr png.*.dll && exit /b 1 || exit /b 0' From 04c24cdd71a8d0f56b03cc4e4dda79ad01bf4742 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 3 Dec 2015 15:11:24 +0100 Subject: [PATCH 06/16] BLD: also find newer freetype --- setupext.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setupext.py b/setupext.py index a59be00c85ab..a95aa248a347 100755 --- a/setupext.py +++ b/setupext.py @@ -957,7 +957,10 @@ def check(self): return "Using local version for testing" if sys.platform == 'win32': - check_include_file(get_include_dirs(), 'ft2build.h', 'freetype') + try: + check_include_file(get_include_dirs(), 'ft2build.h', 'freetype') + except CheckFailed: + check_include_file(get_include_dirs(), 'freetype2\\ft2build.h', 'freetype') return 'Using unknown version found on system.' status, output = getstatusoutput("freetype-config --ftversion") From affc50e369e1b99568f00cf8076fca857ff47ef0 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sat, 23 Jan 2016 21:16:29 +0100 Subject: [PATCH 07/16] Win: build with a local freetype Makes it possible to use the local freetype config option in setup.cfg. Parts of this commit are from https://github.com/jbmohler/matplotlib-winbuild --- build_alllocal.cmd | 32 ++++++ setup_external_compile.py | 228 ++++++++++++++++++++++++++++++++++++++ setupext.py | 75 ++++++++++--- 3 files changed, 318 insertions(+), 17 deletions(-) create mode 100644 build_alllocal.cmd create mode 100644 setup_external_compile.py diff --git a/build_alllocal.cmd b/build_alllocal.cmd new file mode 100644 index 000000000000..e7d2a00670ab --- /dev/null +++ b/build_alllocal.cmd @@ -0,0 +1,32 @@ +:: This assumes you have installed all the dependencies via conda packages: +:: # create a new environment with the required packages +:: conda create -n "matplotlib_build" python=3.4 numpy python-dateutil pyparsing pytz tornado pyqt cycler tk libpng zlib freetype +:: activate matplotlib_build +:: # this package is only available in the conda-forge channel +:: conda install -c conda-forge msinttypes + +set TARGET=bdist_wheel +IF [%1]==[] ( + echo Using default target: %TARGET% +) else ( + set TARGET=%1 + echo Using user supplied target: %TARGET% +) + +IF NOT DEFINED CONDA_DEFAULT_ENV ( + echo No Conda env activated: you need to create a conda env with the right packages and activate it! + GOTO:eof +) + +:: copy the libs which have "wrong" names +set LIBRARY_LIB=%CONDA_DEFAULT_ENV%\Library\lib +mkdir lib || cmd /c "exit /b 0" +copy %LIBRARY_LIB%\zlibstatic.lib lib\z.lib +copy %LIBRARY_LIB%\libpng_static.lib lib\png.lib + +:: Make the header files and the rest of the static libs available during the build +:: CONDA_DEFAULT_ENV is a env variable which is set to the currently active environment path +set MPLBASEDIRLIST=%CONDA_DEFAULT_ENV%\Library\;. + +:: build the target +python setup.py %TARGET% diff --git a/setup_external_compile.py b/setup_external_compile.py new file mode 100644 index 000000000000..25b7dcfb5325 --- /dev/null +++ b/setup_external_compile.py @@ -0,0 +1,228 @@ +""" +This file extracts and builds library dependencies libpng, zlib, & freetype2 on +MS Windows. It also extract tcl/tk for the header files. + +There are four possible build targets -- one for each permutation of VS 2008, +2010 and 32/64 bit. This builds the configuration that matches the Python +install that is executing. + +For Python 2.6, 2.7, 3.2: + +- VS 2008, 32 bit -- Windows SDK v7.0 +- VS 2008, 64 bit -- Windows SDK v7.0 + +For Python 3.3, 3.4: + +- VS 2010, 32 bit -- Windows SDK v7.1 +- VS 2010, 64 bit -- Windows SDK v7.1 +""" + +from __future__ import print_function, absolute_import +import sys +import platform +import os +import glob +import shutil +import zipfile +import tarfile +import distutils.msvc9compiler as msvc + +def fixproj(project_file, bit_target): + """ + :param bit_target: one of 'Win32' or 'x64' + """ + with open(project_file, 'r') as fd: + content = '\n'.join(line.strip() for line in fd if line.strip()) + content = content.replace('Win32', bit_target).replace('x64', bit_target) + with open(project_file, 'w') as fd: + fd.write(content) + +def tar_extract(tar_file, target): + with tarfile.open(tar_file, 'r:gz') as tgz: + tgz.extractall(target) + +def zip_extract(zip_file, target): + with zipfile.ZipFile(zip_file) as zf: + zf.extractall(target) + +# Configuration selection & declaration: +DEPSSRC = os.path.join(os.path.dirname(os.path.normpath(__file__)), 'deps_source') +DEPSBUILD = os.path.join(os.path.dirname(os.path.normpath(__file__)), 'build') +X64 = platform.architecture()[0] == '64bit' +PYVER = sys.version_info[:2] +VS2010 = PYVER >= (3, 3) +# If not VS2010, then use VS2008 + +VCVARSALL = None + +def prepare_build_cmd(build_cmd, **kwargs): + global VCVARSALL + if VCVARSALL == None: + candidate = msvc.find_vcvarsall(10.0 if VS2010 else 9.0) + if candidate == None: + raise RuntimeError('Microsoft VS {} required'.format('2010' if VS2010 else '2008')) + else: + VCVARSALL = candidate + + return build_cmd.format(vcvarsall=VCVARSALL, xXX='x64' if X64 else 'x86', **kwargs) + +def config_dir(): + segment = 'msvcr{}-x{}'.format('100' if VS2010 else '90', '64' if X64 else '32') + return os.path.join(DEPSBUILD, segment) + +def tcl_config_dir(): + return os.path.join(config_dir(), 'tcl85', 'include') + +def build_tcl(): + inclib = config_dir() + tcl_inclib = tcl_config_dir() + if not os.path.exists(tcl_inclib): + os.makedirs(tcl_inclib) + tcl_inclib_x11 = os.path.join(tcl_inclib, 'X11') + if not os.path.exists(tcl_inclib_x11): + os.makedirs(tcl_inclib_x11) + + distfile = os.path.join(DEPSSRC, 'tcl8513-src.zip') + compfile = os.path.join(tcl_inclib, 'tcl.h') + if not os.path.exists(compfile) or os.path.getmtime(distfile) > os.path.getmtime(compfile): + zip_extract(distfile, DEPSBUILD) + targetdir = os.path.join(DEPSBUILD, 'tcl8.5.13') + headers = glob.glob(os.path.join(targetdir, 'generic', '*.h')) + for filename in headers: + shutil.copy(filename, tcl_inclib) + + distfile = os.path.join(DEPSSRC, 'tk8513-src.zip') + compfile = os.path.join(tcl_inclib, 'tk.h') + if not os.path.exists(compfile) or os.path.getmtime(distfile) > os.path.getmtime(compfile): + zip_extract(distfile, DEPSBUILD) + targetdir = os.path.join(DEPSBUILD, 'tk8.5.13') + headers = glob.glob(os.path.join(targetdir, 'generic', '*.h')) + for filename in headers: + shutil.copy(filename, tcl_inclib) + headers = glob.glob(os.path.join(targetdir, 'xlib', 'X11', '*.*')) + for filename in headers: + shutil.copy(filename, tcl_inclib_x11) + +ZLIB_BUILD_CMD = """\ +@ECHO OFF +REM call "%ProgramFiles%\\Microsoft SDKs\\Windows\\v7.0\\Bin\\SetEnv.Cmd" /Release /{xXX} /xp +call "{vcvarsall}" {xXX} + +cd /D %ZLIB% +nmake -f win32\\Makefile.msc clean +nmake -f win32\\Makefile.msc +copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B zlib.lib %INCLIB%\\z.lib +copy /Y /B zlib.h %INCLIB% +copy /Y /B zconf.h %INCLIB% +""" + +def build_zlib(): + inclib = config_dir() + if not os.path.exists(inclib): + os.makedirs(inclib) + + distfile = os.path.join(DEPSSRC, 'zlib128.zip') + compfile = os.path.join(inclib, 'z.lib') + if os.path.exists(compfile) and os.path.getmtime(distfile) < os.path.getmtime(compfile): + # already built + return + + zip_extract(distfile, DEPSBUILD) + + cmdfile = os.path.join(DEPSBUILD, 'build_zlib.cmd') + with open(cmdfile, 'w') as cmd: + cmd.write(prepare_build_cmd(ZLIB_BUILD_CMD)) + + os.environ['INCLIB'] = inclib + os.environ['ZLIB'] = os.path.join(DEPSBUILD, 'zlib-1.2.8') + os.system(cmdfile) + +LIBPNG_BUILD_CMD = """\ +@ECHO OFF +REM call "%ProgramFiles%\\Microsoft SDKs\\Windows\\v7.0\\Bin\\SetEnv.Cmd" /Release /{xXX} /xp +call "{vcvarsall}" {xXX} +set CMAKE="cmake.exe" + +set BUILDDIR=%LIBPNG%-build +rd /S /Q %BUILDDIR% +%CMAKE% -G"NMake Makefiles" -H%LIBPNG% -B%BUILDDIR% ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DZLIB_INCLUDE_DIR=%INCLIB% ^ + -DZLIB_LIBRARY:FILEPATH=%INCLIB%\\zlib.lib ^ + -DPNG_STATIC=ON ^ + -DPNG_SHARED=OFF +copy /Y /B %BUILDDIR%\\pnglibconf.h %INCLIB% +copy /Y /B %LIBPNG%\\png.h %INCLIB% +copy /Y /B %LIBPNG%\\pngconf.h %INCLIB% +cd %BUILDDIR% +nmake -f Makefile +REM It's a static lib -- no *.dll in sight! +REM copy /Y /B *.dll %INCLIB% +copy /Y /B *.lib %INCLIB% +copy /Y /B libpng16_static.lib %INCLIB%\\png.lib +""" + +def build_libpng(): + inclib = config_dir() + if not os.path.exists(inclib): + os.mkdir(inclib) + + distfile = os.path.join(DEPSSRC, 'libpng-1.6.7.tar.gz') + compfile = os.path.join(inclib, 'png.lib') + if os.path.exists(compfile) and os.path.getmtime(distfile) < os.path.getmtime(compfile): + # already built + return + + tar_extract(distfile, DEPSBUILD) + + cmdfile = os.path.join(DEPSBUILD, 'build_libpng.cmd') + with open(cmdfile, 'w') as cmd: + cmd.write(prepare_build_cmd(LIBPNG_BUILD_CMD)) + + os.environ['INCLIB'] = inclib + os.environ['LIBPNG'] = os.path.join(DEPSBUILD, 'libpng-1.6.7') + os.system(cmdfile) + +FREETYPE_VERSION = '2.4.11' + +FREETYPE_BUILD_CMD = """\ +@ECHO OFF +REM 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\\win32\\{vc20xx}\\freetype.sln /t:Clean;Build /p:Configuration="{config}";Platform={WinXX} +xcopy /Y /E /Q %FREETYPE%\\include %INCLIB% +xcopy /Y /E /Q %FREETYPE%\\objs\\win32\\{vc20xx} %INCLIB% +copy /Y /B %FREETYPE%\\objs\\win32\\{vc20xx}\\*.lib %INCLIB%\\freetype.lib +""" + +def build_freetype(): + inclib = config_dir() + if not os.path.exists(inclib): + os.mkdir(inclib) + + distfile = os.path.join(DEPSSRC, 'ft2411.zip') + compfile = os.path.join(inclib, 'freetype.lib') + if os.path.exists(compfile) and os.path.getmtime(distfile) < os.path.getmtime(compfile): + # already built + return + + vc = 'vc2010' if VS2010 else 'vc2008' + WinXX = 'x64' if X64 else 'Win32' + + zip_extract(distfile, DEPSBUILD) + ft_dir = os.path.join(DEPSBUILD, 'freetype-2.4.11') + fixproj(os.path.join(ft_dir, 'builds', 'win32', vc, 'freetype.sln'), WinXX) + fixproj(os.path.join(ft_dir, 'builds', 'win32', vc, 'freetype.{}'.format('vcxproj' if VS2010 else 'vcproj')), WinXX) + + cmdfile = os.path.join(DEPSBUILD, '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['INCLIB'] = inclib + os.environ['FREETYPE'] = ft_dir + os.system(cmdfile) \ No newline at end of file diff --git a/setupext.py b/setupext.py index a95aa248a347..f8a891492695 100755 --- a/setupext.py +++ b/setupext.py @@ -61,7 +61,8 @@ def _get_xdg_cache_dir(): # This is the version of FreeType to use when building a local # version. It must match the value in -# lib/matplotlib.__init__.py +# 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' # md5 hash of the freetype tarball LOCAL_FREETYPE_HASH = '348e667d728c597360e4a87c16556597' @@ -171,16 +172,16 @@ def get_base_dirs(): """ 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 of the local interperter, + # on conda windows, we also add the \Library of the local interperter, # as conda installs libs/includes there if os.getenv('CONDA_DEFAULT_ENV'): win_bases.append(os.path.join(os.getenv('CONDA_DEFAULT_ENV'), "Library")) - + basedir_map = { 'win32': win_bases, 'darwin': ['/usr/local/', '/usr', '/usr/X11', @@ -198,7 +199,7 @@ def get_include_dirs(): """ 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 + # 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)) @@ -1010,8 +1011,12 @@ def add_flags(self, ext): # 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.a')) + 0, os.path.join(src_path, 'objs', '.libs', libfreetype)) ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'local')) else: pkg_config.setup_extension( @@ -1034,8 +1039,12 @@ def do_custom_build(self): 'build', 'freetype-{0}'.format(LOCAL_FREETYPE_VERSION)) # We've already built freetype - if os.path.isfile( - os.path.join(src_path, 'objs', '.libs', 'libfreetype.a')): + if sys.platform == 'win32': + libfreetype = 'libfreetype.lib' + else: + libfreetype = 'libfreetype.a' + + if os.path.isfile(os.path.join(src_path, 'objs', '.libs', libfreetype)): return tarball = 'freetype-{0}.tar.gz'.format(LOCAL_FREETYPE_VERSION) @@ -1107,15 +1116,47 @@ def do_custom_build(self): "{0} does not match expected hash.".format(tarball)) print("Building {0}".format(tarball)) - cflags = 'CFLAGS="{0} -fPIC" '.format(os.environ.get('CFLAGS', '')) - - subprocess.check_call( - ['tar', 'zxf', tarball], cwd='build') - 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) + if sys.platform != 'win32': + # compilation on all other platforms than windows + cflags = 'CFLAGS="{0} -fPIC" '.format(os.environ.get('CFLAGS', '')) + + subprocess.check_call( + ['tar', 'zxf', tarball], cwd='build') + 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} +:: 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 +""" + from setup_external_compile import fixproj, prepare_build_cmd, VS2010, X64, tar_extract + vc = 'vc2010' if VS2010 else 'vc2008' + vc = 'vc2010' if VS2010 else 'vc2008' + WinXX = 'x64' if X64 else 'Win32' + tar_extract(tarball_path, "build") + + #fixproj(os.path.join(src_path, 'builds', 'windows', vc, 'freetype.sln'), WinXX) + #fixproj(os.path.join(src_path, 'builds', 'windows', vc, 'freetype.{}'.format( + # 'vcxproj' if VS2010 else '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): From 696f215a5d1fb182bd8a1644164425f9f2eee94f Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sat, 23 Jan 2016 21:57:01 +0100 Subject: [PATCH 08/16] CI: enable local freetype and use conda forge functools on py27 --- build_alllocal.cmd | 2 ++ ci/conda_recipe/meta.yaml | 1 - setupext.py | 7 ++++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/build_alllocal.cmd b/build_alllocal.cmd index e7d2a00670ab..37560d1830de 100644 --- a/build_alllocal.cmd +++ b/build_alllocal.cmd @@ -4,6 +4,8 @@ :: activate matplotlib_build :: # this package is only available in the conda-forge channel :: conda install -c conda-forge msinttypes +:: if you build on py2.7: +:: conda install -c conda-forge functools32 set TARGET=bdist_wheel IF [%1]==[] ( diff --git a/ci/conda_recipe/meta.yaml b/ci/conda_recipe/meta.yaml index 5f992efff571..88cf1cfd6fd1 100644 --- a/ci/conda_recipe/meta.yaml +++ b/ci/conda_recipe/meta.yaml @@ -19,7 +19,6 @@ source: # https://github.com/conda/conda-build/issues/1061 - condaversion.patch - requirements: build: - python diff --git a/setupext.py b/setupext.py index f8a891492695..42084096a91d 100755 --- a/setupext.py +++ b/setupext.py @@ -1135,10 +1135,15 @@ def do_custom_build(self): 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 +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\\{WinXX}\\{vc20xx}\\freetype261.lib %FREETYPE%\\objs\\.libs\\libfreetype.lib +) """ from setup_external_compile import fixproj, prepare_build_cmd, VS2010, X64, tar_extract vc = 'vc2010' if VS2010 else 'vc2008' From e2a4e0bbb92d8559e32c967f103e31718dd982b1 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 25 Jan 2016 19:51:12 +0100 Subject: [PATCH 09/16] CI/Win: also build a local freetype on py27 64bit --- setupext.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/setupext.py b/setupext.py index 42084096a91d..dbc398bd518e 100755 --- a/setupext.py +++ b/setupext.py @@ -1142,18 +1142,18 @@ def do_custom_build(self): 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\\{WinXX}\\{vc20xx}\\freetype261.lib %FREETYPE%\\objs\\.libs\\libfreetype.lib + copy %FREETYPE%\\objs\\win32\\{vc20xx}\\freetype261.lib %FREETYPE%\\objs\\.libs\\libfreetype.lib ) """ from setup_external_compile import fixproj, prepare_build_cmd, VS2010, X64, tar_extract - vc = 'vc2010' if VS2010 else 'vc2008' + # 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' tar_extract(tarball_path, "build") - - #fixproj(os.path.join(src_path, 'builds', 'windows', vc, 'freetype.sln'), WinXX) - #fixproj(os.path.join(src_path, 'builds', 'windows', vc, 'freetype.{}'.format( - # 'vcxproj' if VS2010 else 'vcproj')), WinXX) + # 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: From 22fb8ac113ae2d0237b889b46faddae15da76c6b Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 27 Jan 2016 20:35:16 +0100 Subject: [PATCH 10/16] TST: up tolerance for test_wedge_range affected: matplotlib.tests.test_patches.test_wedge_range.test (RMS 0.059) (x64,27) matplotlib.tests.test_patches.test_wedge_range.test (RMS 0.059) (x64,34) matplotlib.tests.test_patches.test_wedge_range.test (RMS 0.059) (x86,27) it seems that only the middle figure in the last row is different. Up the tolerance on windows to let the tests pass. --- lib/matplotlib/tests/test_patches.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 44856642b282..f844431d58d1 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -20,6 +20,9 @@ from matplotlib import path as mpath from matplotlib import transforms as mtrans +import sys +on_win = (sys.platform == 'win32') + def test_Polygon_close(): #: Github issue #1018 identified a bug in the Polygon handling @@ -250,7 +253,7 @@ def test_wedge_movement(): @image_comparison(baseline_images=['wedge_range'], - remove_text=True) + remove_text=True, tol=0.06 if on_win else 0) def test_wedge_range(): ax = plt.axes() From fdf35a09fb35393307265dc4033dfa36710a19fb Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 9 Jun 2016 15:04:43 +0200 Subject: [PATCH 11/16] TST: increase tolerance for test_patches.test_wedge_range --- lib/matplotlib/tests/test_patches.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index f844431d58d1..3cd550bd5ea8 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -252,8 +252,9 @@ def test_wedge_movement(): assert_equal(getattr(w, attr), new_v) +# png needs tol>=0.06, pdf tol>=1.617 @image_comparison(baseline_images=['wedge_range'], - remove_text=True, tol=0.06 if on_win else 0) + remove_text=True, tol=1.65 if on_win else 0) def test_wedge_range(): ax = plt.axes() From d4687c911a100b98fdc10c0e9a0fff7acd774eee Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Wed, 24 Aug 2016 17:02:38 -0700 Subject: [PATCH 12/16] MAINT: remove baseline images from Windows wheel Add and use script to remove baseline test images from Windows wheel. --- appveyor.yml | 3 +++ tools/rm_test_images.py | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tools/rm_test_images.py diff --git a/appveyor.yml b/appveyor.yml index bf16836d932e..be26f17ae2d4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -147,6 +147,9 @@ after_test: # Build the wheel with the static libs # Hide the output, the copied files really clutter the build log... - cmd: '%CMD_IN_ENV% python setup.py bdist_wheel > NUL:' + # Delete the result images from the wheel + - pip install delocate + - python tools\rm_test_images.py dist\*.whl # And now the conda build after a cleanup... # cleanup build files so that they don't pollute the conda build but keep the wheel in dist... diff --git a/tools/rm_test_images.py b/tools/rm_test_images.py new file mode 100644 index 000000000000..f53c1b12ff95 --- /dev/null +++ b/tools/rm_test_images.py @@ -0,0 +1,47 @@ +#!/usr/bin/env +""" Remove test images from matplotlib wheel(s) +""" +from __future__ import print_function + +from os.path import join as pjoin, basename, abspath, isdir +from shutil import rmtree +from argparse import ArgumentParser + +IMAGE_PATH = pjoin('matplotlib', 'tests', 'baseline_images') + +from delocate.wheeltools import InWheelCtx + + +def rm_images(whl_fname, out_fname, verbose=False): + whl_fname = abspath(whl_fname) + out_fname = abspath(out_fname) + with InWheelCtx(whl_fname) as ctx: + if not isdir(IMAGE_PATH): + if verbose: + print('No {} in {}'.format(IMAGE_PATH, whl_fname)) + return + rmtree(IMAGE_PATH) + # Write the wheel + ctx.out_wheel = out_fname + + +def get_parser(): + parser = ArgumentParser() + parser.add_argument('whl_fnames', nargs='+') + parser.add_argument('--verbose', action='store_true') + parser.add_argument('--out-path') + return parser + + +def main(): + args = get_parser().parse_args() + for whl_fname in args.whl_fnames: + out_fname = (pjoin(args.out_path, basename(whl_fname)) if args.out_path + else whl_fname) + if args.verbose: + print('Removing test images from {}'.format(whl_fname)) + rm_images(whl_fname, out_fname, verbose=args.verbose) + + +if __name__ == "__main__": + main() From bd10463514fda1dc215461b9f8b6d112eac907ee Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Wed, 24 Aug 2016 18:56:37 -0700 Subject: [PATCH 13/16] BF: fix finding of wheel with wildcard cmd shell does not do wildcard expansion. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index be26f17ae2d4..372d4812dab6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -149,7 +149,7 @@ after_test: - cmd: '%CMD_IN_ENV% python setup.py bdist_wheel > NUL:' # Delete the result images from the wheel - pip install delocate - - python tools\rm_test_images.py dist\*.whl + - cmd: for %%w in ("dist\*.whl") do python tools\rm_test_images.py %%w # And now the conda build after a cleanup... # cleanup build files so that they don't pollute the conda build but keep the wheel in dist... From 95f1e72f8552bfd27d079ae026f71e2f8fea53cf Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 27 Jan 2016 20:40:17 +0100 Subject: [PATCH 14/16] TST: up tolerance for test_tri_smooth_gradient affected: * matplotlib.tests.test_triangulation.test_tri_smooth_gradient.test (RMS 0.014) (x64,35) The diff looks pitch black to me... -> up the tolerance... --- lib/matplotlib/tests/test_triangulation.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 06e56a3c6230..49a7d40baffb 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -14,6 +14,8 @@ import matplotlib.cm as cm from matplotlib.path import Path +import sys +on_win = (sys.platform == 'win32') def test_delaunay(): # No duplicate points, regular grid. @@ -770,7 +772,8 @@ def z(x, y): @image_comparison(baseline_images=['tri_smooth_gradient'], - extensions=['png'], remove_text=True) + extensions=['png'], remove_text=True, + tol=0.015 if on_win else 0) def test_tri_smooth_gradient(): # Image comparison based on example trigradient_demo. From 71372e710401c503306e99015cad048b99754b1c Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Tue, 26 Jan 2016 23:39:38 +0100 Subject: [PATCH 15/16] TST: some tex tests also need to guard against missing gs Also fixed a problem in an error message when bytes were appended to a string (fails on py3.x) by using %. One of the errors before: ====================================================================== ERROR: matplotlib.tests.test_backend_ps.test_savefig_to_stringio_with_usetex ---------------------------------------------------------------------- Traceback (most recent call last): File "lib\site-packages\nose\case.py", line 198, in runTest self.test(*self.arg) File "lib\matplotlib\testing\decorators.py", line 152, in wrapped_callable func(*args, **kwargs) File "lib\matplotlib\testing\decorators.py", line 55, in failer result = f(*args, **kwargs) File "lib\matplotlib\tests\test_backend_ps.py", line 77, in test_savefig_to_stringio_with_usetex _test_savefig_to_stringio() File "lib\matplotlib\tests\test_backend_ps.py", line 40, in _test_savefig_to_stringio fig.savefig(buffer, format=format) File "lib\matplotlib\figure.py", line 1698, in savefig self.canvas.print_figure(*args, **kwargs) File "lib\matplotlib\backend_bases.py", line 2232, in print_figure **kwargs) File "lib\matplotlib\backends\backend_ps.py", line 985, in print_ps return self._print_ps(outfile, 'ps', *args, **kwargs) File "lib\matplotlib\backends\backend_ps.py", line 1012, in _print_ps **kwargs) File "lib\matplotlib\backends\backend_ps.py", line 1376, in _print_figure_tex rotated=psfrag_rotated) File "lib\matplotlib\backends\backend_ps.py", line 1539, in gs_distill raise RuntimeError(m % output) RuntimeError: ghostscript was not able to process your image. Here is the full report generated by ghostscript: b'' --- lib/matplotlib/backends/backend_ps.py | 9 +++++++-- lib/matplotlib/tests/test_backend_ps.py | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 7a0b983acaa6..b5d284cfffbd 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -1503,8 +1503,13 @@ def gs_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False): with io.open(outfile, 'rb') as fh: if exit_status: - raise RuntimeError('ghostscript was not able to process \ - your image.\nHere is the full report generated by ghostscript:\n\n' + fh.read()) + output = fh.read() + m = "\n".join(["ghostscript was not able to process your image.", + "Here is the full report generated by ghostscript:", + "", + "%s"]) + # use % to prevent problems with bytes + raise RuntimeError(m % output) else: verbose.report(fh.read(), 'debug') os.remove(outfile) diff --git a/lib/matplotlib/tests/test_backend_ps.py b/lib/matplotlib/tests/test_backend_ps.py index ae12c501ba14..f017164ff7bb 100644 --- a/lib/matplotlib/tests/test_backend_ps.py +++ b/lib/matplotlib/tests/test_backend_ps.py @@ -71,6 +71,7 @@ def test_savefig_to_stringio_with_distiller(): @cleanup @needs_tex +@needs_ghostscript def test_savefig_to_stringio_with_usetex(): matplotlib.rcParams['text.latex.unicode'] = True matplotlib.rcParams['text.usetex'] = True @@ -90,6 +91,7 @@ def test_savefig_to_stringio_eps_afm(): @cleanup @needs_tex +@needs_ghostscript def test_savefig_to_stringio_with_usetex_eps(): matplotlib.rcParams['text.latex.unicode'] = True matplotlib.rcParams['text.usetex'] = True From 83b840778a49f8d0b3bf33d963ae3cb2fafd5b4b Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Tue, 26 Jan 2016 22:02:32 +0100 Subject: [PATCH 16/16] TST: if no converter is found, skip the test --- lib/matplotlib/testing/compare.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/testing/compare.py b/lib/matplotlib/testing/compare.py index 1d476d40e975..d5a41a611f7c 100644 --- a/lib/matplotlib/testing/compare.py +++ b/lib/matplotlib/testing/compare.py @@ -174,8 +174,8 @@ def convert(filename, cache): """ base, extension = filename.rsplit('.', 1) if extension not in converter: - raise ImageComparisonFailure( - "Don't know how to convert %s files to png" % extension) + from nose import SkipTest + raise SkipTest("Don't know how to convert %s files to png" % extension) newname = base + '_' + extension + '.png' if not os.path.exists(filename): raise IOError("'%s' does not exist" % filename)