diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000000..372d4812dab6 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,185 @@ +# 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:' + # Delete the result images from the wheel + - pip install delocate + - 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... + - 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/build_alllocal.cmd b/build_alllocal.cmd new file mode 100644 index 000000000000..37560d1830de --- /dev/null +++ b/build_alllocal.cmd @@ -0,0 +1,34 @@ +:: 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 +:: if you build on py2.7: +:: conda install -c conda-forge functools32 + +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/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..88cf1cfd6fd1 --- /dev/null +++ b/ci/conda_recipe/meta.yaml @@ -0,0 +1,73 @@ +# 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 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/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) 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 diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 44856642b282..3cd550bd5ea8 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 @@ -249,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) + remove_text=True, tol=1.65 if on_win else 0) def test_wedge_range(): ax = plt.axes() 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. 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 5b84c0a4f121..dbc398bd518e 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' @@ -172,8 +173,17 @@ 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, + # 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', ], @@ -188,8 +198,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 @@ -945,7 +958,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") @@ -995,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( @@ -1019,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) @@ -1092,15 +1116,52 @@ 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} +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, tar_extract + # 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") + # 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): 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() 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() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy