summaryrefslogtreecommitdiff
path: root/var/spack/repos/builtin/packages/python/package.py
diff options
context:
space:
mode:
Diffstat (limited to 'var/spack/repos/builtin/packages/python/package.py')
-rw-r--r--var/spack/repos/builtin/packages/python/package.py230
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)