diff options
Diffstat (limited to 'var/spack/repos/builtin/packages/python/package.py')
-rw-r--r-- | var/spack/repos/builtin/packages/python/package.py | 230 |
1 files changed, 151 insertions, 79 deletions
diff --git a/var/spack/repos/builtin/packages/python/package.py b/var/spack/repos/builtin/packages/python/package.py index c77756a78f..6d8823cc64 100644 --- a/var/spack/repos/builtin/packages/python/package.py +++ b/var/spack/repos/builtin/packages/python/package.py @@ -1,27 +1,8 @@ -############################################################################## -# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. +# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. # -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files for our notice and the LGPL. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License (as -# published by the Free Software Foundation) version 2.1, February 1999. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and -# conditions of the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -############################################################################## +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + import ast import os import platform @@ -30,12 +11,14 @@ import sys import llnl.util.tty as tty from llnl.util.lang import match_predicate -from llnl.util.filesystem import force_remove +from llnl.util.filesystem import (force_remove, get_filetype, + path_contains_subdirectory) -import spack -from spack import * -from spack.util.prefix import Prefix +import spack.store import spack.util.spack_json as sjson +from spack.util.environment import is_system_path +from spack.util.prefix import Prefix +from spack import * class Python(AutotoolsPackage): @@ -46,6 +29,10 @@ class Python(AutotoolsPackage): list_url = "https://www.python.org/downloads/" list_depth = 1 + version('3.7.0', '41b6595deb4147a1ed517a7d9a580271') + version('3.6.5', 'ab25d24b1f8cc4990ade979f6dc37883') + version('3.6.4', '9de6494314ea199e3633211696735f65') + version('3.6.3', 'e9180c69ed9a878a4a8a3ab221e32fa9') version('3.6.2', 'e1a36bfffdd1d3a780b1825daf16e56c') version('3.6.1', '2d0fc9f3a5940707590e07f03ecb08b9') version('3.6.0', '3f7062ccf8be76491884d0e47ac8b251') @@ -56,7 +43,8 @@ class Python(AutotoolsPackage): version('3.3.6', 'cdb3cd08f96f074b3f3994ccb51063e9') version('3.2.6', '23815d82ae706e9b781ca65865353d39') version('3.1.5', '02196d3fc7bc76bdda68aa36b0dd16ab') - version('2.7.14', 'cee2e4b33ad3750da77b2e85f2f8b724', preferred=True) + version('2.7.15', '045fb3440219a1f6923fefdabde63342', preferred=True) + version('2.7.14', 'cee2e4b33ad3750da77b2e85f2f8b724') version('2.7.13', '17add4bf0ad0ec2f08e0cae6d205c700') version('2.7.12', '88d61f82e3616a4be952828b3694109d') version('2.7.11', '6b6076ec9e93f05dd63e47eb9c15728b') @@ -83,6 +71,18 @@ class Python(AutotoolsPackage): variant('pic', default=True, description='Produce position-independent code (for shared libs)') + variant('dbm', default=True, description='Provide support for dbm') + variant( + 'optimizations', + default=False, + description='Enable expensive build-time optimizations, if available' + ) + # See https://legacy.python.org/dev/peps/pep-0394/ + variant('pythoncmd', default=True, + description="Symlink 'python3' executable to 'python' " + "(not PEP 394 compliant)") + + depends_on("pkgconfig", type="build") depends_on("openssl") depends_on("bzip2") depends_on("readline") @@ -91,6 +91,11 @@ class Python(AutotoolsPackage): depends_on("zlib") depends_on("tk", when="+tk") depends_on("tcl", when="+tk") + depends_on("gdbm", when='+dbm') + + # https://docs.python.org/3/whatsnew/3.7.html#build-changes + depends_on("libffi", when="@3.7:") + depends_on("openssl@1.0.2:", when="@3.7:") # Patch does not work for Python 3.1 patch('ncurses.patch', when='@:2.8,3.2:') @@ -99,10 +104,25 @@ class Python(AutotoolsPackage): patch('cray-rpath-2.3.patch', when="@2.3:3.0.1 platform=cray") patch('cray-rpath-3.1.patch', when="@3.1:3.99 platform=cray") + # Fixes an alignment problem with more aggressive optimization in gcc8 + # https://github.com/python/cpython/commit/0b91f8a668201fc58fa732b8acc496caedfdbae0 + patch('gcc-8-2.7.14.patch', when="@2.7.14 %gcc@8:") + + # For more information refer to this bug report: + # https://bugs.python.org/issue29712 + conflicts( + '@:2.8 +shared', + when='+optimizations', + msg='+optimizations is incompatible with +shared in python@2.X' + ) + _DISTUTIL_VARS_TO_SAVE = ['LDSHARED'] _DISTUTIL_CACHE_FILENAME = 'sysconfig.json' _distutil_vars = None + # An in-source build with --enable-optimizations fails for python@3.X + build_directory = 'spack-build' + @when('@2.7:2.8,3.4:') def patch(self): # NOTE: Python's default installation procedure makes it possible for a @@ -117,7 +137,6 @@ class Python(AutotoolsPackage): def setup_environment(self, spack_env, run_env): spec = self.spec - prefix = self.prefix # TODO: The '--no-user-cfg' option for Python installation is only in # Python v2.7 and v3.4+ (see https://bugs.python.org/issue1180) and @@ -128,8 +147,6 @@ class Python(AutotoolsPackage): 'user configurations are present.').format(self.version)) # Need this to allow python build to find the Python installation. - spack_env.set('PYTHONHOME', prefix) - spack_env.set('PYTHONPATH', prefix) spack_env.set('MACOSX_DEPLOYMENT_TARGET', platform.mac_ver()[0]) def configure_args(self): @@ -137,15 +154,45 @@ class Python(AutotoolsPackage): # setup.py needs to be able to read the CPPFLAGS and LDFLAGS # as it scans for the library and headers to build - dep_pfxs = [dspec.prefix for dspec in spec.dependencies('link')] - config_args = [ - '--with-threads', - 'CPPFLAGS=-I{0}'.format(' -I'.join(dp.include for dp in dep_pfxs)), - 'LDFLAGS=-L{0}'.format(' -L'.join(dp.lib for dp in dep_pfxs)), - ] + link_deps = spec.dependencies('link') + + # Header files are often included assuming they reside in a + # subdirectory of prefix.include, e.g. #include <openssl/ssl.h>, + # which is why we don't use HeaderList here. The header files of + # libffi reside in prefix.lib but the configure script of Python + # finds them using pkg-config. + cppflags = '-I' + ' -I'.join(dep.prefix.include + for dep in link_deps + if dep.name != 'libffi') + + # Currently, the only way to get SpecBuildInterface wrappers of the + # dependencies (which we need to get their 'libs') is to get them + # using spec.__getitem__. + ldflags = ' '.join(spec[dep.name].libs.search_flags + for dep in link_deps) + + config_args = ['CPPFLAGS=' + cppflags, 'LDFLAGS=' + ldflags] + + # https://docs.python.org/3/whatsnew/3.7.html#build-changes + if spec.satisfies('@:3.6'): + config_args.append('--with-threads') + + if '^libffi' in spec: + config_args.append('--with-system-ffi') + else: + config_args.append('--without-system-ffi') + + if spec.satisfies('@2.7.13:2.8,3.5.3:', strict=True) \ + and '+optimizations' in spec: + config_args.append('--enable-optimizations') + if spec.satisfies('%gcc platform=darwin'): config_args.append('--disable-toolbox-glue') + if spec.satisfies('%intel', strict=True) and \ + spec.satisfies('@2.7.12:2.8,3.5.2:', strict=True): + config_args.append('--with-icc') + if '+shared' in spec: config_args.append('--enable-shared') else: @@ -202,6 +249,12 @@ class Python(AutotoolsPackage): os.symlink(os.path.join(src, f), os.path.join(dst, f)) + if spec.satisfies('@3:') and spec.satisfies('+pythoncmd'): + os.symlink(os.path.join(prefix.bin, 'python3'), + os.path.join(prefix.bin, 'python')) + os.symlink(os.path.join(prefix.bin, 'python3-config'), + os.path.join(prefix.bin, 'python-config')) + # TODO: Once better testing support is integrated, add the following tests # https://wiki.python.org/moin/TkInter # @@ -352,37 +405,32 @@ class Python(AutotoolsPackage): on the version of Python and how it was installed. In general, Python 2 comes with ``python`` and ``python2`` commands, - while Python 3 only comes with a ``python3`` command. + while Python 3 only comes with a ``python3`` command. However, some + package managers will symlink ``python`` to ``python3``, while others + may contain ``python3.6``, ``python3.5``, and ``python3.4`` in the + same directory. - :returns: The Python command - :rtype: Executable + Returns: + Executable: the Python command """ # We need to be careful here. If the user is using an externally - # installed python, all 3 commands could be in the same directory. - - # Search for `python2` iff using Python 2 - if (self.spec.satisfies('@:2') and - os.path.exists(os.path.join(self.prefix.bin, 'python2'))): - command = 'python2' - # Search for `python3` iff using Python 3 - elif (self.spec.satisfies('@3:') and - os.path.exists(os.path.join(self.prefix.bin, 'python3'))): - command = 'python3' - # If neither were found, try `python` - elif os.path.exists(os.path.join(self.prefix.bin, 'python')): - command = 'python' + # installed python, several different commands could be located + # in the same directory. Be as specific as possible. Search for: + # + # * python3.6 + # * python3 + # * python + # + # in that order if using python@3.6.5, for example. + version = self.spec.version + for ver in [version.up_to(2), version.up_to(1), '']: + path = os.path.join(self.prefix.bin, 'python{0}'.format(ver)) + if os.path.exists(path): + return Executable(path) else: msg = 'Unable to locate {0} command in {1}' raise RuntimeError(msg.format(self.name, self.prefix.bin)) - # The python command may be a symlink if it was installed - # with Homebrew. Since some packages try to determine the - # location of libraries and headers based on the path, - # return the realpath - path = os.path.realpath(os.path.join(self.prefix.bin, command)) - - return Executable(path) - def print_string(self, string): """Returns the appropriate print string depending on the version of Python. @@ -461,7 +509,7 @@ class Python(AutotoolsPackage): return LibraryList(os.path.join(frameworkprefix, ldlibrary)) else: msg = 'Unable to locate {0} libraries in {1}' - raise RuntimeError(msg.format(self.name, libdir)) + raise RuntimeError(msg.format(ldlibrary, libdir)) else: library = self.get_config_var('LIBRARY') @@ -471,7 +519,7 @@ class Python(AutotoolsPackage): return LibraryList(os.path.join(frameworkprefix, library)) else: msg = 'Unable to locate {0} libraries in {1}' - raise RuntimeError(msg.format(self.name, libdir)) + raise RuntimeError(msg.format(library, libdir)) @property def headers(self): @@ -504,11 +552,19 @@ class Python(AutotoolsPackage): """Set PYTHONPATH to include the site-packages directory for the extension and any other python extensions it depends on.""" + # If we set PYTHONHOME, we must also ensure that the corresponding + # python is found in the build environment. This to prevent cases + # where a system provided python is run against the standard libraries + # of a Spack built python. See issue #7128 spack_env.set('PYTHONHOME', self.home) + path = os.path.dirname(self.command.path) + if not is_system_path(path): + spack_env.prepend_path('PATH', path) + python_paths = [] for d in dependent_spec.traverse( - deptype=('build', 'run'), deptype_query='run'): + deptype=('build', 'run', 'test')): if d.package.extends(self.spec): python_paths.append(join_path(d.prefix, self.site_packages_dir)) @@ -628,33 +684,49 @@ class Python(AutotoolsPackage): "sys.path[p:p]=new; " "sys.__egginsert = p+len(new)\n") - def activate(self, ext_pkg, **args): + def activate(self, ext_pkg, view, **args): ignore = self.python_ignore(ext_pkg, args) args.update(ignore=ignore) - extensions_layout = args.get("extensions_layout", - spack.store.extensions) - - super(Python, self).activate(ext_pkg, **args) + super(Python, self).activate(ext_pkg, view, **args) + extensions_layout = view.extensions_layout exts = extensions_layout.extension_map(self.spec) exts[ext_pkg.name] = ext_pkg.spec - self.write_easy_install_pth( - exts, - prefix=extensions_layout.extendee_target_directory(self)) + self.write_easy_install_pth(exts, prefix=view.root) - def deactivate(self, ext_pkg, **args): + def deactivate(self, ext_pkg, view, **args): args.update(ignore=self.python_ignore(ext_pkg, args)) - super(Python, self).deactivate(ext_pkg, **args) - extensions_layout = args.get("extensions_layout", - spack.store.extensions) + super(Python, self).deactivate(ext_pkg, view, **args) + extensions_layout = view.extensions_layout exts = extensions_layout.extension_map(self.spec) # Make deactivate idempotent if ext_pkg.name in exts: del exts[ext_pkg.name] - self.write_easy_install_pth( - exts, - prefix=extensions_layout.extendee_target_directory(self)) + self.write_easy_install_pth(exts, prefix=view.root) + + def add_files_to_view(self, view, merge_map): + bin_dir = self.spec.prefix.bin + for src, dst in merge_map.items(): + if not path_contains_subdirectory(src, bin_dir): + view.link(src, dst) + elif not os.path.islink(src): + copy(src, dst) + if 'script' in get_filetype(src): + filter_file( + self.spec.prefix, os.path.abspath(view.root), dst) + else: + orig_link_target = os.path.realpath(src) + new_link_target = os.path.abspath(merge_map[orig_link_target]) + view.link(new_link_target, dst) + + def remove_files_from_view(self, view, merge_map): + bin_dir = self.spec.prefix.bin + for src, dst in merge_map.items(): + if not path_contains_subdirectory(src, bin_dir): + view.remove_file(src, dst) + else: + os.remove(dst) |