From 0166abe61a7b44faa8ad9b6c7f1e4b029f73f9d8 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 29 Dec 2018 16:18:55 +0100 Subject: [PATCH 1/7] Remove now unused {has,check}_include_file, _check_for_pkg_config. --- setupext.py | 63 ----------------------------------------------------- 1 file changed, 63 deletions(-) diff --git a/setupext.py b/setupext.py index 5ff2c9dc165e..59556fa1b2a0 100644 --- a/setupext.py +++ b/setupext.py @@ -205,28 +205,6 @@ def write_cache(local_fn, data): options['local_freetype'] = lft or options.get('local_freetype', False) -def has_include_file(include_dirs, filename): - """ - Returns `True` if *filename* can be found in one of the - directories in *include_dirs*. - """ - if sys.platform == 'win32': - include_dirs = [*include_dirs, # Don't modify it in-place. - *os.environ.get('INCLUDE', '.').split(os.pathsep)] - return any(pathlib.Path(dir, filename).exists() for dir in include_dirs) - - -def check_include_file(include_dirs, filename, package): - """ - Raises an exception if the given include file can not be found. - """ - if not has_include_file(include_dirs, filename): - raise CheckFailed( - "The C/C++ header for %s (%s) could not be found. You " - "may need to install the development package." % - (package, filename)) - - def get_base_dirs(): """ Returns a list of standard base directories on this platform. @@ -523,47 +501,6 @@ def get_setup_requires(self): """ return [] - def _check_for_pkg_config(self, package, include_file, min_version=None, - version=None): - """ - A convenience function for writing checks for a - pkg_config-defined dependency. - - `package` is the pkg_config package name. - - `include_file` is a top-level include file we expect to find. - - `min_version` is the minimum version required. - - `version` will override the found version if this package - requires an alternate method for that. Set version='unknown' - if the version is not known but you still want to disabled - pkg_config version check. - """ - if version is None: - version = pkg_config.get_version(package) - - if version is None: - raise CheckFailed( - "pkg-config information for '%s' could not be found." % - package) - - if min_version and version != 'unknown': - if not is_min_version(version, min_version): - raise CheckFailed( - "Requires %s %s or later. Found %s." % - (package, min_version, version)) - - ext = self.get_extension() - if ext is None: - ext = make_extension('test', []) - pkg_config.setup_extension(ext, package) - - check_include_file( - ext.include_dirs + get_include_dirs(), include_file, package) - - return 'version %s' % version - def do_custom_build(self): """ If a package needs to do extra custom things, such as building a From 2ec27bd1f08358c62753b1a15355d3a807ef46a4 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 29 Dec 2018 16:41:33 +0100 Subject: [PATCH 2/7] Don't parse the flags from pkg-config, just pass everything through. --- setupext.py | 98 +++++++++++++++++++---------------------------------- 1 file changed, 34 insertions(+), 64 deletions(-) diff --git a/setupext.py b/setupext.py index 59556fa1b2a0..cdfadbf0ff28 100644 --- a/setupext.py +++ b/setupext.py @@ -11,6 +11,7 @@ import pathlib import platform import setuptools +import shlex import shutil import subprocess import sys @@ -321,20 +322,17 @@ def get_buffer_hash(fd): class PkgConfig(object): - """ - This is a class for communicating with pkg-config. - """ + """This is a class for communicating with pkg-config.""" + def __init__(self): - """ - Determines whether pkg-config exists on this machine. - """ - if sys.platform == 'win32': - self.has_pkgconfig = False - else: - self.pkg_config = os.environ.get('PKG_CONFIG', 'pkg-config') - self.set_pkgconfig_path() - self.has_pkgconfig = shutil.which(self.pkg_config) is not None - if not self.has_pkgconfig: + """Determines whether pkg-config exists on this machine.""" + self.pkg_config = None + if sys.platform != 'win32': + pkg_config = os.environ.get('PKG_CONFIG', 'pkg-config') + if shutil.which(pkg_config) is not None: + self.pkg_config = pkg_config + self.set_pkgconfig_path() + else: print("IMPORTANT WARNING:\n" " pkg-config is not installed.\n" " matplotlib may not be able to find some of its dependencies") @@ -353,52 +351,28 @@ def set_pkgconfig_path(self): except KeyError: os.environ['PKG_CONFIG_PATH'] = pkgconfig_path - def setup_extension(self, ext, package, default_include_dirs=[], - default_library_dirs=[], default_libraries=[], - alt_exec=None): - """ - Add parameters to the given `ext` for the given `package`. - """ - flag_map = { - '-I': 'include_dirs', '-L': 'library_dirs', '-l': 'libraries'} - - executable = alt_exec - if self.has_pkgconfig: - executable = (self.pkg_config + ' {0}').format(package) - - use_defaults = True - - if executable is not None: - command = "{0} --libs --cflags ".format(executable) - + def setup_extension(self, ext, package, + alt_exec=None, default_libraries=()): + """Add parameters to the given *ext* for the given *package*.""" + cmd = ([self.pkg_config, package] if self.pkg_config + else alt_exec) + if cmd is not None: try: - output = subprocess.check_output( - command, shell=True, stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: + flags = shlex.split(subprocess.check_output( + [*cmd, "--cflags", "--libs"], universal_newlines=True)) + except (OSError, subprocess.CalledProcessError): pass else: - output = output.decode(sys.getfilesystemencoding()) - use_defaults = False - for token in output.split(): - attr = flag_map.get(token[:2]) - if attr is not None: - getattr(ext, attr).insert(0, token[2:]) - - if use_defaults: - basedirs = get_base_dirs() - for base in basedirs: - for include in default_include_dirs: - dir = os.path.join(base, include) - if os.path.exists(dir): - ext.include_dirs.append(dir) - for lib in default_library_dirs: - dir = os.path.join(base, lib) - if os.path.exists(dir): - ext.library_dirs.append(dir) - ext.libraries.extend(default_libraries) - return True - - return False + # In theory, one could call pkg-config separately with --cflags + # and --libs and use them to fill extra_compile_args and + # extra_link_args respectively, but keeping all the flags + # together works as well and makes it simpler to handle + # libpng-config, which has --ldflags instead of --libs. + ext.extra_compile_args.extend(flags) + ext.extra_link_args.extend(flags) + return + # Else, fall back on the defaults. + ext.libraries.extend(default_libraries) def get_version(self, package): """ @@ -806,12 +780,7 @@ def add_flags(self, ext): else: pkg_config.setup_extension( ext, 'freetype2', - default_include_dirs=[ - 'include/freetype2', 'freetype2', - 'lib/freetype2/include', - 'lib/freetype2/include/freetype2'], - default_library_dirs=[ - 'freetype2/lib'], + alt_exec=['freetype-config', '--cflags', '--libs'], default_libraries=['freetype', 'z']) ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'system')) @@ -954,8 +923,9 @@ def get_extension(self): ] ext = make_extension('matplotlib._png', sources) pkg_config.setup_extension( - ext, 'libpng', default_libraries=['png', 'z'], - alt_exec='libpng-config --ldflags') + ext, 'libpng', + alt_exec=['libpng-config', '--cflags', '--ldflags'], + default_libraries=['png', 'z']) Numpy().add_flags(ext) return ext From d56e916cb6ac1c5cbd124281f5c5d611d031a7b9 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 29 Dec 2018 16:54:01 +0100 Subject: [PATCH 3/7] Move _tri extension to src/. --- setupext.py | 4 ++-- {lib/matplotlib => src}/tri/_tri.cpp | 0 {lib/matplotlib => src}/tri/_tri.h | 0 {lib/matplotlib => src}/tri/_tri_wrapper.cpp | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename {lib/matplotlib => src}/tri/_tri.cpp (100%) rename {lib/matplotlib => src}/tri/_tri.h (100%) rename {lib/matplotlib => src}/tri/_tri_wrapper.cpp (100%) diff --git a/setupext.py b/setupext.py index cdfadbf0ff28..d44f75c266d6 100644 --- a/setupext.py +++ b/setupext.py @@ -1023,8 +1023,8 @@ class Tri(SetupPackage): def get_extension(self): sources = [ - "lib/matplotlib/tri/_tri.cpp", - "lib/matplotlib/tri/_tri_wrapper.cpp", + "src/tri/_tri.cpp", + "src/tri/_tri_wrapper.cpp", "src/mplutils.cpp" ] ext = make_extension('matplotlib._tri', sources) diff --git a/lib/matplotlib/tri/_tri.cpp b/src/tri/_tri.cpp similarity index 100% rename from lib/matplotlib/tri/_tri.cpp rename to src/tri/_tri.cpp diff --git a/lib/matplotlib/tri/_tri.h b/src/tri/_tri.h similarity index 100% rename from lib/matplotlib/tri/_tri.h rename to src/tri/_tri.h diff --git a/lib/matplotlib/tri/_tri_wrapper.cpp b/src/tri/_tri_wrapper.cpp similarity index 100% rename from lib/matplotlib/tri/_tri_wrapper.cpp rename to src/tri/_tri_wrapper.cpp From 72b38e8d67699c6d779a0f852e5ee05681b41217 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 29 Dec 2018 16:59:04 +0100 Subject: [PATCH 4/7] Don't explicitly pass the default include and lib paths. --- .appveyor.yml | 2 +- build_alllocal.cmd | 4 -- setup.cfg.template | 5 -- setupext.py | 124 ++++++++++----------------------------- src/_contour.cpp | 4 +- src/_contour.h | 2 +- src/_contour_wrapper.cpp | 8 +-- src/tri/_tri.h | 2 +- src/tri/_tri_wrapper.cpp | 4 +- 9 files changed, 42 insertions(+), 113 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b60e1f6133cb..cbc7e687af8d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -84,7 +84,6 @@ install: # and for the wheels - del %LIBRARY_LIB%\png.lib - del %LIBRARY_LIB%\z.lib - - set MPLBASEDIRLIST=%CONDA_PREFIX%\Library\;. # enables the local freetype build - set MPLLOCALFREETYPE=1 # Show the installed packages + versions @@ -92,6 +91,7 @@ install: test_script: # Now build the thing.. + - set LINK=/LIBPATH:%cd%\lib - pip install -ve . # these should show no z, png, or freetype dll... - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" diff --git a/build_alllocal.cmd b/build_alllocal.cmd index 4a3aba3a0275..535042e08a66 100644 --- a/build_alllocal.cmd +++ b/build_alllocal.cmd @@ -23,9 +23,5 @@ 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_PREFIX is a env variable which is set to the currently active environment path -set MPLBASEDIRLIST=%CONDA_PREFIX%\Library\;. - :: build the target python setup.py %TARGET% diff --git a/setup.cfg.template b/setup.cfg.template index 3b17cb94761d..b7b1be0a425e 100644 --- a/setup.cfg.template +++ b/setup.cfg.template @@ -3,11 +3,6 @@ [egg_info] -[directories] -# Uncomment to override the default basedir in setupext.py. -# This can be a single directory or a comma-delimited list of directories. -#basedirlist = /usr - [test] # If you plan to develop Matplotlib and run or add to the test suite, # set this to True. It will download and build a specific version of diff --git a/setupext.py b/setupext.py index d44f75c266d6..579a82d1388b 100644 --- a/setupext.py +++ b/setupext.py @@ -177,7 +177,6 @@ def write_cache(local_fn, data): options = { 'display_status': True, 'backend': None, - 'basedirlist': None } @@ -192,11 +191,6 @@ def write_cache(local_fn, data): if config.has_option('rc_options', 'backend'): options['backend'] = config.get("rc_options", "backend") - if config.has_option('directories', 'basedirlist'): - options['basedirlist'] = [ - x.strip() for x in - config.get("directories", "basedirlist").split(',')] - if config.has_option('test', 'local_freetype'): options['local_freetype'] = config.getboolean("test", "local_freetype") else: @@ -206,52 +200,6 @@ def write_cache(local_fn, data): options['local_freetype'] = lft or options.get('local_freetype', False) -def get_base_dirs(): - """ - Returns a list of standard base directories on this platform. - """ - if options['basedirlist']: - return options['basedirlist'] - - if os.environ.get('MPLBASEDIRLIST'): - return os.environ.get('MPLBASEDIRLIST').split(os.pathsep) - - win_bases = ['win32_static'] - # on conda windows, we also add the \Library, - # as conda installs libs/includes there - # env var names mess: https://github.com/conda/conda/issues/2312 - conda_env_path = os.getenv('CONDA_PREFIX') # conda >= 4.1 - if not conda_env_path: - conda_env_path = os.getenv('CONDA_DEFAULT_ENV') # conda < 4.1 - if conda_env_path and os.path.isdir(conda_env_path): - win_bases.append(os.path.join(conda_env_path, "Library")) - - basedir_map = { - 'win32': win_bases, - 'darwin': ['/usr/local/', '/usr', '/usr/X11', - '/opt/X11', '/opt/local'], - 'sunos5': [os.getenv('MPLIB_BASE') or '/usr/local', ], - 'gnu0': ['/usr'], - 'aix5': ['/usr/local'], - } - return basedir_map.get(sys.platform, ['/usr/local', '/usr']) - - -def get_include_dirs(): - """ - Returns a list of standard include directories on this platform. - """ - include_dirs = [os.path.join(d, 'include') for d in get_base_dirs()] - if sys.platform != 'win32': - # gcc includes these dirs automatically, so also look for headers in - # these dirs - include_dirs.extend( - os.environ.get('CPATH', '').split(os.pathsep)) - include_dirs.extend( - os.environ.get('CPLUS_INCLUDE_PATH', '').split(os.pathsep)) - return include_dirs - - def is_min_version(found, minversion): """ Returns whether *found* is a version at least as high as *minversion*. @@ -285,32 +233,6 @@ def print_line(*args, **kwargs): print_status = print_message = print_raw = print_line -def make_extension(name, files, *args, **kwargs): - """ - Make a new extension. Automatically sets include_dirs and - library_dirs to the base directories appropriate for this - platform. - - `name` is the name of the extension. - - `files` is a list of source files. - - Any additional arguments are passed to the - `distutils.core.Extension` constructor. - """ - ext = Extension(name, files, *args, **kwargs) - for dir in get_base_dirs(): - include_dir = os.path.join(dir, 'include') - if os.path.exists(include_dir): - ext.include_dirs.append(include_dir) - for lib in ('lib', 'lib64'): - lib_dir = os.path.join(dir, lib) - if os.path.exists(lib_dir): - ext.library_dirs.append(lib_dir) - ext.include_dirs.append('.') - return ext - - def get_buffer_hash(fd): BLOCKSIZE = 1 << 16 hasher = hashlib.sha256() @@ -354,8 +276,10 @@ def set_pkgconfig_path(self): def setup_extension(self, ext, package, alt_exec=None, default_libraries=()): """Add parameters to the given *ext* for the given *package*.""" - cmd = ([self.pkg_config, package] if self.pkg_config - else alt_exec) + + # First, try to get the flags from pkg-config. + + cmd = ([self.pkg_config, package] if self.pkg_config else alt_exec) if cmd is not None: try: flags = shlex.split(subprocess.check_output( @@ -371,7 +295,21 @@ def setup_extension(self, ext, package, ext.extra_compile_args.extend(flags) ext.extra_link_args.extend(flags) return - # Else, fall back on the defaults. + + # If that fails, fall back on the defaults. + + # conda Windows header and library paths. + # https://github.com/conda/conda/issues/2312 re: getting the env dir. + if sys.platform == 'win32': + conda_env_path = (os.getenv('CONDA_PREFIX') # conda >= 4.1 + or os.getenv('CONDA_DEFAULT_ENV')) # conda < 4.1 + if conda_env_path and os.path.isdir(conda_env_path): + ext.include_dirs.append(os.fspath( + pathlib.Path(conda_env_path, "Library/include"))) + ext.library_dirs.append(os.fspath( + pathlib.Path(conda_env_path, "Library/lib"))) + + # Default linked libs. ext.libraries.extend(default_libraries) def get_version(self, package): @@ -897,7 +835,7 @@ def get_extension(self): 'src/mplutils.cpp', 'src/py_converters.cpp', ] - ext = make_extension('matplotlib.ft2font', sources) + ext = Extension('matplotlib.ft2font', sources) FreeType().add_flags(ext) Numpy().add_flags(ext) LibAgg().add_flags(ext, add_sources=False) @@ -921,7 +859,7 @@ def get_extension(self): 'src/_png.cpp', 'src/mplutils.cpp', ] - ext = make_extension('matplotlib._png', sources) + ext = Extension('matplotlib._png', sources) pkg_config.setup_extension( ext, 'libpng', alt_exec=['libpng-config', '--cflags', '--ldflags'], @@ -953,7 +891,7 @@ def get_extension(self): 'extern/ttconv/pprdrv_tt2.cpp', 'extern/ttconv/ttutil.cpp' ] - ext = make_extension('matplotlib.ttconv', sources) + ext = Extension('matplotlib.ttconv', sources) Numpy().add_flags(ext) ext.include_dirs.insert(0, 'extern') return ext @@ -968,7 +906,7 @@ def get_extension(self): 'src/_path_wrapper.cpp' ] - ext = make_extension('matplotlib._path', sources) + ext = Extension('matplotlib._path', sources) Numpy().add_flags(ext) LibAgg().add_flags(ext) return ext @@ -984,7 +922,7 @@ def get_extension(self): 'src/_image_wrapper.cpp', 'src/py_converters.cpp' ] - ext = make_extension('matplotlib._image', sources) + ext = Extension('matplotlib._image', sources) Numpy().add_flags(ext) LibAgg().add_flags(ext) @@ -1000,7 +938,7 @@ def get_extension(self): "src/_contour_wrapper.cpp", 'src/py_converters.cpp', ] - ext = make_extension('matplotlib._contour', sources) + ext = Extension('matplotlib._contour', sources) Numpy().add_flags(ext) LibAgg().add_flags(ext, add_sources=False) return ext @@ -1011,8 +949,8 @@ class QhullWrap(SetupPackage): def get_extension(self): sources = ['src/qhull_wrap.c'] - ext = make_extension('matplotlib._qhull', sources, - define_macros=[('MPL_DEVNULL', os.devnull)]) + ext = Extension('matplotlib._qhull', sources, + define_macros=[('MPL_DEVNULL', os.devnull)]) Numpy().add_flags(ext) Qhull().add_flags(ext) return ext @@ -1027,7 +965,7 @@ def get_extension(self): "src/tri/_tri_wrapper.cpp", "src/mplutils.cpp" ] - ext = make_extension('matplotlib._tri', sources) + ext = Extension('matplotlib._tri', sources) Numpy().add_flags(ext) return ext @@ -1043,7 +981,7 @@ def get_extension(self): "src/_backend_agg.cpp", "src/_backend_agg_wrapper.cpp" ] - ext = make_extension('matplotlib.backends._backend_agg', sources) + ext = Extension('matplotlib.backends._backend_agg', sources) Numpy().add_flags(ext) LibAgg().add_flags(ext) FreeType().add_flags(ext) @@ -1063,7 +1001,7 @@ def get_extension(self): 'src/py_converters.cpp', ] - ext = make_extension('matplotlib.backends._tkagg', sources) + ext = Extension('matplotlib.backends._tkagg', sources) self.add_flags(ext) Numpy().add_flags(ext) LibAgg().add_flags(ext, add_sources=False) @@ -1094,7 +1032,7 @@ def get_extension(self): 'src/_macosx.m' ] - ext = make_extension('matplotlib.backends._macosx', sources) + ext = Extension('matplotlib.backends._macosx', sources) ext.extra_link_args.extend(['-framework', 'Cocoa']) if platform.python_implementation().lower() == 'pypy': ext.extra_compile_args.append('-DPYPY=1') diff --git a/src/_contour.cpp b/src/_contour.cpp index d870fb2d151a..ac655d73de27 100644 --- a/src/_contour.cpp +++ b/src/_contour.cpp @@ -6,8 +6,8 @@ // to the appropriate make_extension call in setupext.py, and then rebuilding. #define NO_IMPORT_ARRAY -#include "src/mplutils.h" -#include "src/_contour.h" +#include "mplutils.h" +#include "_contour.h" #include diff --git a/src/_contour.h b/src/_contour.h index fc7b2f106df4..6232b3abd2a7 100644 --- a/src/_contour.h +++ b/src/_contour.h @@ -142,7 +142,7 @@ #ifndef MPL_CONTOUR_H #define MPL_CONTOUR_H -#include "src/numpy_cpp.h" +#include "numpy_cpp.h" #include #include #include diff --git a/src/_contour_wrapper.cpp b/src/_contour_wrapper.cpp index 8aa64fcdf068..6464a3748daf 100644 --- a/src/_contour_wrapper.cpp +++ b/src/_contour_wrapper.cpp @@ -1,7 +1,7 @@ -#include "src/_contour.h" -#include "src/mplutils.h" -#include "src/py_converters.h" -#include "src/py_exceptions.h" +#include "_contour.h" +#include "mplutils.h" +#include "py_converters.h" +#include "py_exceptions.h" /* QuadContourGenerator */ diff --git a/src/tri/_tri.h b/src/tri/_tri.h index 7243e195f9a8..13aad01fb668 100644 --- a/src/tri/_tri.h +++ b/src/tri/_tri.h @@ -63,7 +63,7 @@ #ifndef MPL_TRI_H #define MPL_TRI_H -#include "src/numpy_cpp.h" +#include "../numpy_cpp.h" #include #include diff --git a/src/tri/_tri_wrapper.cpp b/src/tri/_tri_wrapper.cpp index 38ce2e55d36d..81fe1d40ba92 100644 --- a/src/tri/_tri_wrapper.cpp +++ b/src/tri/_tri_wrapper.cpp @@ -1,6 +1,6 @@ #include "_tri.h" -#include "src/mplutils.h" -#include "src/py_exceptions.h" +#include "../mplutils.h" +#include "../py_exceptions.h" /* Triangulation */ From e6ca9ee6d92796a0290eab0e2335444064868016 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 29 Dec 2018 17:12:29 +0100 Subject: [PATCH 5/7] Restore pkg-config version checks. --- setupext.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/setupext.py b/setupext.py index 579a82d1388b..a889db729177 100644 --- a/setupext.py +++ b/setupext.py @@ -273,8 +273,9 @@ def set_pkgconfig_path(self): except KeyError: os.environ['PKG_CONFIG_PATH'] = pkgconfig_path - def setup_extension(self, ext, package, - alt_exec=None, default_libraries=()): + def setup_extension( + self, ext, package, + atleast_version=None, alt_exec=None, default_libraries=()): """Add parameters to the given *ext* for the given *package*.""" # First, try to get the flags from pkg-config. @@ -282,6 +283,9 @@ def setup_extension(self, ext, package, cmd = ([self.pkg_config, package] if self.pkg_config else alt_exec) if cmd is not None: try: + if self.pkg_config and atleast_version: + subprocess.check_call( + [*cmd, f"--atleast-version={atleast_version}"]) flags = shlex.split(subprocess.check_output( [*cmd, "--cflags", "--libs"], universal_newlines=True)) except (OSError, subprocess.CalledProcessError): @@ -312,19 +316,6 @@ def setup_extension(self, ext, package, # Default linked libs. ext.libraries.extend(default_libraries) - def get_version(self, package): - """ - Get the version of the package from pkg-config. - """ - if not self.has_pkgconfig: - return None - - status, output = subprocess.getstatusoutput( - self.pkg_config + " %s --modversion" % (package)) - if status == 0: - return output - return None - # The PkgConfig class should be used through this singleton pkg_config = PkgConfig() @@ -717,7 +708,11 @@ def add_flags(self, ext): ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'local')) else: pkg_config.setup_extension( + # FreeType 2.3 has libtool version 9.11.3 as can be checked + # from the tarball. For FreeType>=2.4, there is a conversion + # table in docs/VERSIONS.txt in the FreeType source tree. ext, 'freetype2', + atleast_version='9.11.3', alt_exec=['freetype-config', '--cflags', '--libs'], default_libraries=['freetype', 'z']) ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'system')) @@ -862,6 +857,7 @@ def get_extension(self): ext = Extension('matplotlib._png', sources) pkg_config.setup_extension( ext, 'libpng', + atleast_version='1.2', alt_exec=['libpng-config', '--cflags', '--ldflags'], default_libraries=['png', 'z']) Numpy().add_flags(ext) From c087542e4d0d2dc3514997252e630a7db82197cd Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 29 Dec 2018 18:26:11 +0100 Subject: [PATCH 6/7] Document how to set build paths. --- INSTALL.rst | 33 +++++++++++++++++++--- doc/api/next_api_changes/2018-12-29-AL.rst | 10 +++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 doc/api/next_api_changes/2018-12-29-AL.rst diff --git a/INSTALL.rst b/INSTALL.rst index 704e76193fef..ec0c74930812 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -174,10 +174,35 @@ etc., you can install the following: .. note:: - Matplotlib depends on non-Python libraries. `pkg-config - `_ can be used - to find required non-Python libraries and thus make the install go more - smoothly if the libraries and headers are not in the expected locations. + Matplotlib depends on non-Python libraries. + + On Linux and OSX, pkg-config_ can be used to find required non-Python + libraries and thus make the install go more smoothly if the libraries and + headers are not in the expected locations. + + .. _pkg-config: https://www.freedesktop.org/wiki/Software/pkg-config/ + + If not using pkg-config (in particular on Windows), you may need to set the + include path (to the FreeType, libpng, and zlib headers) and link path (to + the FreeType, libpng, and zlib libraries) explicitly, if they are not in + standard locations. This can be done using standard environment variables + -- on Linux and OSX: + + .. code-block:: sh + + export CFLAGS='-I/directory/containing/ft2build.h ...' + export LDFLAGS='-L/directory/containing/libfreetype.so ...' + + and on Windows: + + .. code-block:: bat + + set CL=/IC:\directory\containing\ft2build.h ... + set LINK=/LIBPATH:C:\directory\containing\freetype.lib ... + + where ``...`` means "also give, in the same format, the directories + containing ``png.h`` and ``zlib.h`` for the include path, and for + ``libpng.so``/``png.lib`` and ``libz.so``/``z.lib`` for the link path." .. note:: diff --git a/doc/api/next_api_changes/2018-12-29-AL.rst b/doc/api/next_api_changes/2018-12-29-AL.rst new file mode 100644 index 000000000000..949f5c214a4e --- /dev/null +++ b/doc/api/next_api_changes/2018-12-29-AL.rst @@ -0,0 +1,10 @@ +Changes to search paths for FreeType and libpng +``````````````````````````````````````````````` + +The ``MPLBASEDIRLIST`` environment variables and ``basedirlist`` entry in +``setup.cfg`` have no effect anymore. Instead, if building in situations where +FreeType or libpng are not in the compiler or linker's default path, set the +standard environment variables ``CFLAGS``/``LDFLAGS`` on Linux or OSX, or +``CL``/``LINK`` on Windows, to indicate the relevant paths. + +See details in :file:`INSTALL.rst`. From 3653f7e58e2b31c0754fcb2b4b34e2f099b17e33 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 29 Dec 2018 18:26:11 +0100 Subject: [PATCH 7/7] Fix flags encodings and redundant flags to {freetype,libpng}-config. --- setupext.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/setupext.py b/setupext.py index a889db729177..ecdf769e3423 100644 --- a/setupext.py +++ b/setupext.py @@ -286,18 +286,19 @@ def setup_extension( if self.pkg_config and atleast_version: subprocess.check_call( [*cmd, f"--atleast-version={atleast_version}"]) - flags = shlex.split(subprocess.check_output( - [*cmd, "--cflags", "--libs"], universal_newlines=True)) + # Use sys.getfilesystemencoding() to allow round-tripping + # when passed back to later subprocess calls; do not use + # locale.getpreferredencoding() which universal_newlines=True + # would do. + cflags = shlex.split( + os.fsdecode(subprocess.check_output([*cmd, "--cflags"]))) + libs = shlex.split( + os.fsdecode(subprocess.check_output([*cmd, "--libs"]))) except (OSError, subprocess.CalledProcessError): pass else: - # In theory, one could call pkg-config separately with --cflags - # and --libs and use them to fill extra_compile_args and - # extra_link_args respectively, but keeping all the flags - # together works as well and makes it simpler to handle - # libpng-config, which has --ldflags instead of --libs. - ext.extra_compile_args.extend(flags) - ext.extra_link_args.extend(flags) + ext.extra_compile_args.extend(cflags) + ext.extra_link_args.extend(libs) return # If that fails, fall back on the defaults. @@ -713,7 +714,7 @@ def add_flags(self, ext): # table in docs/VERSIONS.txt in the FreeType source tree. ext, 'freetype2', atleast_version='9.11.3', - alt_exec=['freetype-config', '--cflags', '--libs'], + alt_exec=['freetype-config'], default_libraries=['freetype', 'z']) ext.define_macros.append(('FREETYPE_BUILD_TYPE', 'system')) @@ -858,7 +859,7 @@ def get_extension(self): pkg_config.setup_extension( ext, 'libpng', atleast_version='1.2', - alt_exec=['libpng-config', '--cflags', '--ldflags'], + alt_exec=['libpng-config', '--ldflags'], default_libraries=['png', 'z']) Numpy().add_flags(ext) return ext