summaryrefslogtreecommitdiff
path: root/var
diff options
context:
space:
mode:
authorGregory Lee <lee218@llnl.gov>2016-12-13 13:56:15 -0800
committerGitHub <noreply@github.com>2016-12-13 13:56:15 -0800
commit392ed4f0cc57ccbdb31c99ce21b69831a2820ef9 (patch)
tree02e65d27fa81a8bb36dccc47f5184563074e66bc /var
parentfb809189d3310328602d795b0ce9c77dc3452351 (diff)
parentd8c44189528207dd341766284dd08d0a78bc879f (diff)
downloadspack-392ed4f0cc57ccbdb31c99ce21b69831a2820ef9.tar.gz
spack-392ed4f0cc57ccbdb31c99ce21b69831a2820ef9.tar.bz2
spack-392ed4f0cc57ccbdb31c99ce21b69831a2820ef9.tar.xz
spack-392ed4f0cc57ccbdb31c99ce21b69831a2820ef9.zip
Merge pull request #2506 from skosukhin/pr_python
A couple of updates for python package.
Diffstat (limited to 'var')
-rw-r--r--var/spack/repos/builtin/packages/python/package.py163
1 files changed, 134 insertions, 29 deletions
diff --git a/var/spack/repos/builtin/packages/python/package.py b/var/spack/repos/builtin/packages/python/package.py
index bf34b83c99..010632dbe7 100644
--- a/var/spack/repos/builtin/packages/python/package.py
+++ b/var/spack/repos/builtin/packages/python/package.py
@@ -22,6 +22,7 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
+import ast
import os
import re
from contextlib import closing
@@ -29,15 +30,17 @@ from contextlib import closing
import spack
import llnl.util.tty as tty
from llnl.util.lang import match_predicate
+from llnl.util.filesystem import force_remove
from spack import *
from spack.util.environment import *
+import spack.util.spack_json as sjson
class Python(Package):
"""The Python programming language."""
homepage = "http://www.python.org"
- url = "http://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz"
+ url = "http://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz"
version('3.5.2', '3fe8434643a78630c61c6464fe2e7e72')
version('3.5.1', 'be78e48cdfc1a7ad90efff146dce6cfe')
@@ -54,7 +57,7 @@ class Python(Package):
extendable = True
- variant('tk', default=False, description='Provide support for Tkinter')
+ variant('tk', default=False, description='Provide support for Tkinter')
variant('ucs4', default=False,
description='Enable UCS4 (wide) unicode strings')
# From https://docs.python.org/2/c-api/unicode.html: Python's default
@@ -71,11 +74,15 @@ class Python(Package):
depends_on("ncurses")
depends_on("sqlite")
depends_on("zlib")
- depends_on("tk", when="+tk")
+ depends_on("tk", when="+tk")
depends_on("tcl", when="+tk")
patch('ncurses.patch')
+ _DISTUTIL_VARS_TO_SAVE = ['LDSHARED']
+ _DISTUTIL_CACHE_FILENAME = 'sysconfig.json'
+ _distutil_vars = None
+
@when('@2.7,3.4:')
def patch(self):
# NOTE: Python's default installation procedure makes it possible for a
@@ -132,19 +139,19 @@ class Python(Package):
make()
make('install')
- self.filter_compilers(spec, prefix)
+ self._save_distutil_vars(prefix)
+
+ self.filter_compilers(prefix)
# TODO:
# On OpenSuse 13, python uses <prefix>/lib64/python2.7/lib-dynload/*.so
# instead of <prefix>/lib/python2.7/lib-dynload/*.so. Oddly enough the
# result is that Python can not find modules like cPickle. A workaround
# for now is to symlink to `lib`:
- src = os.path.join(prefix,
- 'lib64',
+ src = os.path.join(prefix.lib64,
'python{0}'.format(self.version.up_to(2)),
'lib-dynload')
- dst = os.path.join(prefix,
- 'lib',
+ dst = os.path.join(prefix.lib,
'python{0}'.format(self.version.up_to(2)),
'lib-dynload')
if os.path.isdir(src) and not os.path.isdir(dst):
@@ -174,7 +181,101 @@ class Python(Package):
# >>> import Tkinter
# >>> Tkinter._test()
- def filter_compilers(self, spec, prefix):
+ def _save_distutil_vars(self, prefix):
+ """
+ Run before changing automatically generated contents of the
+ _sysconfigdata.py, which is used by distutils to figure out what
+ executables to use while compiling and linking extensions. If we build
+ extensions with spack those executables should be spack's wrappers.
+ Spack partially covers this by setting environment variables that
+ are also accounted for by distutils. Currently there is one more known
+ variable that must be set, which is LDSHARED, so the method saves its
+ autogenerated value to pass it to the dependant package's setup script.
+ """
+
+ self._distutil_vars = {}
+
+ input_filename = None
+ for filename in [join_path(lib_dir,
+ 'python{0}'.format(self.version.up_to(2)),
+ '_sysconfigdata.py')
+ for lib_dir in [prefix.lib, prefix.lib64]]:
+ if os.path.isfile(filename):
+ input_filename = filename
+ break
+
+ if not input_filename:
+ return
+
+ input_dict = None
+ try:
+ with open(input_filename) as input_file:
+ match = re.search(r'build_time_vars\s*=\s*(?P<dict>{.*})',
+ input_file.read(),
+ flags=re.DOTALL)
+
+ if match:
+ input_dict = ast.literal_eval(match.group('dict'))
+ except (IOError, SyntaxError):
+ pass
+
+ if not input_dict:
+ tty.warn('Failed to find \'build_time_vars\' dictionary in file '
+ '\'%s\'. This might cause the extensions that are '
+ 'installed with distutils to call compilers directly '
+ 'avoiding Spack\'s wrappers.' % input_filename)
+ return
+
+ for var_name in Python._DISTUTIL_VARS_TO_SAVE:
+ if var_name in input_dict:
+ self._distutil_vars[var_name] = input_dict[var_name]
+ else:
+ tty.warn('Failed to find key \'%s\' in \'build_time_vars\' '
+ 'dictionary in file \'%s\'. This might cause the '
+ 'extensions that are installed with distutils to '
+ 'call compilers directly avoiding Spack\'s wrappers.'
+ % (var_name, input_filename))
+
+ if len(self._distutil_vars) > 0:
+ output_filename = None
+ try:
+ output_filename = join_path(
+ spack.store.layout.metadata_path(self.spec),
+ Python._DISTUTIL_CACHE_FILENAME)
+ with open(output_filename, 'w') as output_file:
+ sjson.dump(self._distutil_vars, output_file)
+ except:
+ tty.warn('Failed to save metadata for distutils. This might '
+ 'cause the extensions that are installed with '
+ 'distutils to call compilers directly avoiding '
+ 'Spack\'s wrappers.')
+ # We make the cache empty if we failed to save it to file
+ # to provide the same behaviour as in the case when the cache
+ # is initialized by the method load_distutils_data().
+ self._distutil_vars = {}
+ if output_filename:
+ force_remove(output_filename)
+
+ def _load_distutil_vars(self):
+ # We update and keep the cache unchanged only if the package is
+ # installed.
+ if not self._distutil_vars and self.installed:
+ try:
+ input_filename = join_path(
+ spack.store.layout.metadata_path(self.spec),
+ Python._DISTUTIL_CACHE_FILENAME)
+ if os.path.isfile(input_filename):
+ with open(input_filename) as input_file:
+ self._distutil_vars = sjson.load(input_file)
+ except:
+ pass
+
+ if not self._distutil_vars:
+ self._distutil_vars = {}
+
+ return self._distutil_vars
+
+ def filter_compilers(self, prefix):
"""Run after install to tell the configuration files and Makefiles
to use the compilers that Spack built the package with.
@@ -184,23 +285,21 @@ class Python(Package):
kwargs = {'ignore_absent': True, 'backup': False, 'string': True}
- dirname = join_path(prefix.lib,
- 'python{0}'.format(self.version.up_to(2)))
+ lib_dirnames = [
+ join_path(lib_dir, 'python{0}'.format(self.version.up_to(2))) for
+ lib_dir in [prefix.lib, prefix.lib64]]
- config = 'config'
- if spec.satisfies('@3:'):
- config = 'config-{0}m'.format(self.version.up_to(2))
+ config_dirname = 'config-{0}m'.format(
+ self.version.up_to(2)) if self.spec.satisfies('@3:') else 'config'
- files = [
- '_sysconfigdata.py',
- join_path(config, 'Makefile')
- ]
+ rel_filenames = ['_sysconfigdata.py',
+ join_path(config_dirname, 'Makefile')]
+
+ abs_filenames = [join_path(dirname, filename) for dirname in
+ lib_dirnames for filename in rel_filenames]
- for filename in files:
- filter_file(env['CC'], self.compiler.cc,
- join_path(dirname, filename), **kwargs)
- filter_file(env['CXX'], self.compiler.cxx,
- join_path(dirname, filename), **kwargs)
+ filter_file(env['CC'], self.compiler.cc, *abs_filenames, **kwargs)
+ filter_file(env['CXX'], self.compiler.cxx, *abs_filenames, **kwargs)
# ========================================================================
# Set up environment to make install easy for python extensions.
@@ -272,13 +371,19 @@ class Python(Package):
module.python = Executable(python_path)
module.setup_py = Executable(python_path + ' setup.py --no-user-cfg')
+ distutil_vars = self._load_distutil_vars()
+
+ if distutil_vars:
+ for key, value in distutil_vars.iteritems():
+ module.setup_py.add_default_env(key, value)
+
# Add variables for lib/pythonX.Y and lib/pythonX.Y/site-packages dirs.
- module.python_lib_dir = join_path(ext_spec.prefix,
- self.python_lib_dir)
+ module.python_lib_dir = join_path(ext_spec.prefix,
+ self.python_lib_dir)
module.python_include_dir = join_path(ext_spec.prefix,
self.python_include_dir)
- module.site_packages_dir = join_path(ext_spec.prefix,
- self.site_packages_dir)
+ module.site_packages_dir = join_path(ext_spec.prefix,
+ self.site_packages_dir)
# Make the site packages directory for extensions
if ext_spec.package.is_extension:
@@ -330,8 +435,8 @@ class Python(Package):
continue
if re.search(r'^(import|#)', line):
continue
- if ((ext.name != 'py-setuptools' and
- re.search(r'setuptools.*egg$', line))):
+ if (ext.name != 'py-setuptools' and
+ re.search(r'setuptools.*egg$', line)):
continue
paths.append(line)