diff --git a/.gitignore b/.gitignore index 6db5a64dc88e..c1d4443e76ad 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,20 @@ *.py[ocd] *.so +# Compiled Dependencies # +######################### +windeps/freetype-2.4.11/ +windeps/libpng-1.6.7-build/ +windeps/libpng-1.6.7/ +windeps/zlib-1.2.8/ +windeps/tcl8.5.13/ +windeps/tk8.5.13/ +windeps/msvcr90-x32/ +windeps/msvcr90-x64/ +windeps/msvcr100-x32/ +windeps/msvcr100-x64/ +windeps/build_*.cmd + # Python files # ################ # setup.py working directory diff --git a/INSTALL b/INSTALL index 579bc580efea..1e7ab596628f 100644 --- a/INSTALL +++ b/INSTALL @@ -215,11 +215,12 @@ e.g., if the header to some required library is in Build requirements ================== -These are external packages which you will need to install before -installing matplotlib. If you are building on OSX, see -:ref:`build_osx`. If you are installing dependencies with a package -manager on Linux, you may need to install the development packages -(look for a "-dev" postfix) in addition to the libraries themselves. +These are external packages which you will need to install before installing +matplotlib. If you are building on OSX, see :ref:`build_osx`. If you are +building on Windows, see :ref:`build_windows`. If you are installing +dependencies with a package manager on Linux, you may need to install the +development packages (look for a "-dev" postfix) in addition to the libraries +themselves. .. note:: @@ -261,11 +262,11 @@ six 1.3 or later :term:`dateutil`. libpng 1.2 (or later) - library for loading and saving :term:`PNG` files (`download - `__). libpng requires - zlib. If you are a Windows user, you can ignore this because we - build support into the matplotlib single-click installer - + library for loading and saving :term:`PNG` files + (`download `__). libpng + requires zlib. If you are a Windows user, you can ignore this since + the sources are shipped and built as part of the matplotlib build + scripts. **Optional GUI frameworks** @@ -306,8 +307,8 @@ ImageMagick :term:`freetype` 1.4 (or later) library for reading true type font files. If you are a windows - user, you can ignore this since we build support into the - matplotlib single click installer. + user, you can ignore this since the sources are shipped and built as part + of the matplotlib build scripts. **Required libraries that ship with matplotlib** @@ -337,3 +338,25 @@ can get the libpng and freetype requirements (darwinports, fink, the different OSX version (e.g., 10.4 and 10.5). We recommend that you build the way we do for the OSX release: get the source from the tarball or the git repository and follow the instruction in :file:`README.osx`. + +.. _build_windows: + +Building on Windows +====================== + +The Python shipped from http://www.python.org is compiled with Visual Studio +2008 for versions before 3.3 and Visual Studio 2010 for 3.3 and later. +Python extensions are recommended to be compiled with the same compiler. The +matplotlib `setup.py` looks for the correct version via distutils. The .NET +Framework 4.0 is required for MSBuild, but you likely already have it. + +In addition to Visual Studio, `CMake `_ is required for +building libpng. For building documentation, you will need to install numpydoc +and miktex. The required freetype, zlib, libpng, tcl, & tk source code is +bundled with matplotlib since there is no canonical Windows package manager. + +Instructions as above will build and install in the Python installation used to +launch `setup.py`. Alternatively to build an installation binary that can be +used on other Windows machines with a matching Python, run:: + + python setup.py bdist_wininst diff --git a/doc/faq/installing_faq.rst b/doc/faq/installing_faq.rst index 2cf1cebb7f94..b18975982756 100644 --- a/doc/faq/installing_faq.rst +++ b/doc/faq/installing_faq.rst @@ -145,6 +145,8 @@ line script to the appropriate places. .. note:: Mac OSX users please see the :ref:`build_osx` guide. + Windows users please see the :ref:`build_windows` guide. + Then, if you want to update your matplotlib at any time, just do:: > git pull diff --git a/doc/make.py b/doc/make.py index ceaea5f66679..c81f26abe69c 100755 --- a/doc/make.py +++ b/doc/make.py @@ -250,6 +250,16 @@ def all(): ] for link, target in required_symlinks: + if os.path.isfile(link): + # This is special processing that applies on platforms that don't deal + # with git symlinks -- probably only MS windows. + delete = False + with open(link, 'r') as content: + delete = target == content.read() + if delete: + os.unlink(link) + else: + raise RuntimeError("doc/{} should be a directory or symlink -- it isn't") if not os.path.exists(link): if hasattr(os, 'symlink'): os.symlink(target, link) diff --git a/setupext.py b/setupext.py index 48c4808e1923..858c603f3b51 100755 --- a/setupext.py +++ b/setupext.py @@ -12,6 +12,7 @@ import sys import warnings from textwrap import fill +import setupwin PY3 = (sys.version_info[0] >= 3) @@ -588,6 +589,11 @@ def get_package_data(self): 'style/stylelib/*.mplstyle', ]} + def add_flags(self, ext): + if sys.platform == 'win32': + ext.include_dirs.append(setupwin.config_dir()) + ext.lib_dirs.append(setupwin.config_dir()) + class SampleData(OptionalPackage): """ @@ -927,7 +933,7 @@ class FreeType(SetupPackage): def check(self): if sys.platform == 'win32': - return "Unknown version" + return "building local version {}".format(setupwin.FREETYPE_VERSION) status, output = getstatusoutput("freetype-config --ftversion") if status == 0: @@ -967,19 +973,27 @@ def version_from_header(self): return '.'.join([major, minor, patch]) def add_flags(self, ext): + extra_include_dirs = [] + extra_library_dirs = [] + if sys.platform == 'win32': + extra_include_dirs = [setupwin.config_dir()] + extra_library_dirs = [setupwin.config_dir()] + pkg_config.setup_extension( ext, 'freetype2', default_include_dirs=[ 'freetype2', 'lib/freetype2/include', - 'lib/freetype2/include/freetype2'], + 'lib/freetype2/include/freetype2'] + extra_include_dirs, default_library_dirs=[ - 'freetype2/lib'], + 'freetype2/lib'] + extra_library_dirs, default_libraries=['freetype', 'z'], alt_exec='freetype-config') def get_extension(self): if sys.platform == 'win32': + setupwin.build_freetype() return None + ext = make_extension('freetype2', []) self.add_flags(ext) return ext @@ -1012,12 +1026,22 @@ def check(self): return str(e) + ' Using unknown version.' def get_extension(self): + library_dirs = [] + include_dirs = [] + if sys.platform == 'win32': + setupwin.build_zlib() + setupwin.build_libpng() + library_dirs = [setupwin.config_dir()] + include_dirs = [setupwin.config_dir()] + sources = [ 'src/_png.cpp', 'src/mplutils.cpp' ] ext = make_extension('matplotlib._png', sources) pkg_config.setup_extension( - ext, 'libpng', default_libraries=['png', 'z']) + ext, 'libpng', default_libraries=['png', 'z'], + default_include_dirs=include_dirs, + default_library_dirs=library_dirs) Numpy().add_flags(ext) CXX().add_flags(ext) return ext @@ -1317,6 +1341,9 @@ def check_requirements(self): return "version %s" % tk_v def get_extension(self): + if sys.platform == 'win32': + setupwin.build_tcl() + sources = [ 'src/agg_py_transforms.cpp', 'src/_tkagg.cpp' @@ -1484,6 +1511,7 @@ def hardcoded_tcl_config(self): def add_flags(self, ext): if sys.platform == 'win32': + ext.include_dirs.extend([setupwin.tcl_config_dir()]) major, minor1, minor2, s, tmp = sys.version_info if sys.version_info[0:2] < (3, 4): ext.include_dirs.extend(['win32_static/include/tcl85']) @@ -1491,7 +1519,7 @@ def add_flags(self, ext): else: ext.include_dirs.extend(['win32_static/include/tcl86']) ext.libraries.extend(['tk86t', 'tcl86t']) - ext.library_dirs.extend([os.path.join(sys.prefix, 'dlls')]) + ext.library_dirs.extend([os.path.join(sys.prefix, 'tcl')]) elif sys.platform == 'darwin': # this config section lifted directly from Imaging - thanks to diff --git a/setupwin.py b/setupwin.py new file mode 100644 index 000000000000..7b8c0d3081bc --- /dev/null +++ b/setupwin.py @@ -0,0 +1,227 @@ +""" +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: +DEPSDIR = os.path.join(os.path.dirname(os.path.normpath(__file__)), 'windeps') +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(DEPSDIR, 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(DEPSDIR, '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, DEPSDIR) + targetdir = os.path.join(DEPSDIR, '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(DEPSDIR, '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, DEPSDIR) + targetdir = os.path.join(DEPSDIR, '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.mkdir(inclib) + + distfile = os.path.join(DEPSDIR, '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, DEPSDIR) + + cmdfile = os.path.join(DEPSDIR, '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(DEPSDIR, '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(DEPSDIR, '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, DEPSDIR) + + cmdfile = os.path.join(DEPSDIR, '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(DEPSDIR, '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(DEPSDIR, '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, DEPSDIR) + ft_dir = os.path.join(DEPSDIR, '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(DEPSDIR, '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) diff --git a/windeps/ft2411.zip b/windeps/ft2411.zip new file mode 100644 index 000000000000..aa80f559ab4d Binary files /dev/null and b/windeps/ft2411.zip differ diff --git a/windeps/libpng-1.6.7.tar.gz b/windeps/libpng-1.6.7.tar.gz new file mode 100644 index 000000000000..fffa8e95f3c1 Binary files /dev/null and b/windeps/libpng-1.6.7.tar.gz differ diff --git a/windeps/tcl8513-src.zip b/windeps/tcl8513-src.zip new file mode 100644 index 000000000000..7d7d2011b733 Binary files /dev/null and b/windeps/tcl8513-src.zip differ diff --git a/windeps/tk8513-src.zip b/windeps/tk8513-src.zip new file mode 100644 index 000000000000..6b38723e4f99 Binary files /dev/null and b/windeps/tk8513-src.zip differ diff --git a/windeps/zlib128.zip b/windeps/zlib128.zip new file mode 100644 index 000000000000..d1c4aa6fca93 Binary files /dev/null and b/windeps/zlib128.zip differ