From 28e129b087145e6a6cf4be40c25e76f8d69290e3 Mon Sep 17 00:00:00 2001 From: alalazo Date: Fri, 9 Jun 2017 21:07:20 +0200 Subject: [PATCH] Added mixins to modularize common behaviors across build-systems. Modifications: * added a mixin to filter compiler wrappers from files * modified hdf5, openmpi, mpich, mvapich2 to use it --- lib/spack/spack/__init__.py | 3 + lib/spack/spack/mixins.py | 82 +++++++++++++++++++ lib/spack/spack/package.py | 5 ++ .../repos/builtin/packages/hdf5/package.py | 13 ++- .../repos/builtin/packages/mpich/package.py | 40 ++------- .../builtin/packages/mvapich2/package.py | 39 ++------- .../repos/builtin/packages/openmpi/package.py | 57 +++++-------- var/spack/repos/builtin/packages/r/package.py | 24 +----- 8 files changed, 144 insertions(+), 119 deletions(-) create mode 100644 lib/spack/spack/mixins.py diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index 21af60422c..62ac793d90 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -214,6 +214,9 @@ 'IntelPackage', ] +from spack.mixins import FilterCompilerWrappersPackageMixin +__all__ += ['FilterCompilerWrappersPackageMixin'] + from spack.version import Version, ver __all__ += ['Version', 'ver'] diff --git a/lib/spack/spack/mixins.py b/lib/spack/spack/mixins.py new file mode 100644 index 0000000000..be3c2c3136 --- /dev/null +++ b/lib/spack/spack/mixins.py @@ -0,0 +1,82 @@ +############################################################################## +# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# 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/llnl/spack +# Please also see the LICENSE file 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 +############################################################################## +import os + +import llnl.util.filesystem + + +class FilterCompilerWrappersPackageMixin(object): + """This mixin class registers a callback that filters a list of files + after installation and substitutes hardcoded paths pointing to the Spack + compiler wrappers with the corresponding 'real' compilers. + """ + + #: compiler wrappers to be filtered (needs to be overridden) + compiler_wrappers = [] + + #: phase after which the callback is invoked (default 'install') + filter_phase = 'install' + + def __init__(self): + + attr_name = '_InstallPhase_{0}'.format(self.filter_phase) + + # Here we want to get the attribute directly from the class (noe from + # the instance), so that we can modify it and add the mixin method + phase = getattr(type(self), attr_name) + + # Due to MRO, we may have taken a method from a parent class + # and modifying it may influence other packages in unwanted manners. + # Solve the problem by copying the phase into the most derived class. + setattr(type(self), attr_name, phase.copy()) + phase = getattr(type(self), attr_name) + + phase.run_after.append( + FilterCompilerWrappersPackageMixin.filter_compilers + ) + + super(FilterCompilerWrappersPackageMixin, self).__init__() + + def filter_compilers(self): + """Substitutes any path referring to a Spack compiler wrapper + with the path of the underlying compiler that has been used. + + If this isn't done, the files will have CC, CXX, F77, and FC set + to Spack's generic cc, c++, f77, and f90. We want them to + be bound to whatever compiler they were built with. + """ + + kwargs = {'ignore_absent': True, 'backup': False, 'string': True} + + if self.compiler_wrappers: + x = llnl.util.filesystem.FileFilter(*self.compiler_wrappers) + + x.filter(os.environ['CC'], self.compiler.cc, **kwargs) + x.filter(os.environ['CXX'], self.compiler.cxx, **kwargs) + x.filter(os.environ['F77'], self.compiler.f77, **kwargs) + x.filter(os.environ['FC'], self.compiler.fc, **kwargs) + + # Remove this linking flag if present (it turns RPATH into RUNPATH) + x.filter('-Wl,--enable-new-dtags', '', **kwargs) diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index f7cb3b0253..f0172565bb 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -182,6 +182,9 @@ def _flush_callbacks(check_name): PackageMeta.phase_fmt.format(phase_name), None ) + if phase is not None: + break + attr_dict[PackageMeta.phase_fmt.format( phase_name)] = phase.copy() phase = attr_dict[ @@ -618,6 +621,8 @@ def __init__(self, spec): self.extra_args = {} + super(PackageBase, self).__init__() + def possible_dependencies(self, transitive=True, visited=None): """Return set of possible transitive dependencies of this package. diff --git a/var/spack/repos/builtin/packages/hdf5/package.py b/var/spack/repos/builtin/packages/hdf5/package.py index 6477d9cee2..471326aac7 100644 --- a/var/spack/repos/builtin/packages/hdf5/package.py +++ b/var/spack/repos/builtin/packages/hdf5/package.py @@ -22,12 +22,13 @@ # License along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## -from spack import * import shutil import sys +from spack import * -class Hdf5(AutotoolsPackage): + +class Hdf5(AutotoolsPackage, FilterCompilerWrappersPackageMixin): """HDF5 is a data model, library, and file format for storing and managing data. It supports an unlimited variety of datatypes, and is designed for flexible and efficient I/O and for high volume and complex data. @@ -294,3 +295,11 @@ def check_install(self): print('-' * 80) raise RuntimeError("HDF5 install check failed") shutil.rmtree(checkdir) + + @property + def compiler_wrappers(self): + return [ + join_path(self.prefix.bin, 'h5c++'), + join_path(self.prefix.bin, 'h5cc'), + join_path(self.prefix.bin, 'h5fc'), + ] diff --git a/var/spack/repos/builtin/packages/mpich/package.py b/var/spack/repos/builtin/packages/mpich/package.py index 74234f46ae..0efca4f325 100644 --- a/var/spack/repos/builtin/packages/mpich/package.py +++ b/var/spack/repos/builtin/packages/mpich/package.py @@ -26,7 +26,7 @@ import os -class Mpich(AutotoolsPackage): +class Mpich(AutotoolsPackage, FilterCompilerWrappersPackageMixin): """MPICH is a high performance and widely portable implementation of the Message Passing Interface (MPI) standard.""" @@ -170,33 +170,11 @@ def configure_args(self): return config_args - @run_after('install') - def filter_compilers(self): - """Run after install to make the MPI compilers use the - compilers that Spack built the package with. - - If this isn't done, they'll have CC, CXX, F77, and FC set - to Spack's generic cc, c++, f77, and f90. We want them to - be bound to whatever compiler they were built with.""" - - mpicc = join_path(self.prefix.bin, 'mpicc') - mpicxx = join_path(self.prefix.bin, 'mpicxx') - mpif77 = join_path(self.prefix.bin, 'mpif77') - mpif90 = join_path(self.prefix.bin, 'mpif90') - - # Substitute Spack compile wrappers for the real - # underlying compiler - kwargs = { - 'ignore_absent': True, - 'backup': False, - 'string': True - } - filter_file(env['CC'], self.compiler.cc, mpicc, **kwargs) - filter_file(env['CXX'], self.compiler.cxx, mpicxx, **kwargs) - filter_file(env['F77'], self.compiler.f77, mpif77, **kwargs) - filter_file(env['FC'], self.compiler.fc, mpif90, **kwargs) - - # Remove this linking flag if present - # (it turns RPATH into RUNPATH) - for wrapper in (mpicc, mpicxx, mpif77, mpif90): - filter_file('-Wl,--enable-new-dtags', '', wrapper, **kwargs) + @property + def compiler_wrappers(self): + return [ + join_path(self.prefix.bin, 'mpicc'), + join_path(self.prefix.bin, 'mpicxx'), + join_path(self.prefix.bin, 'mpif77'), + join_path(self.prefix.bin, 'mpif90') + ] diff --git a/var/spack/repos/builtin/packages/mvapich2/package.py b/var/spack/repos/builtin/packages/mvapich2/package.py index 4454907b74..1f90981fda 100644 --- a/var/spack/repos/builtin/packages/mvapich2/package.py +++ b/var/spack/repos/builtin/packages/mvapich2/package.py @@ -35,7 +35,7 @@ def _process_manager_validator(values): ) -class Mvapich2(AutotoolsPackage): +class Mvapich2(AutotoolsPackage, FilterCompilerWrappersPackageMixin): """MVAPICH2 is an MPI implementation for Infiniband networks.""" homepage = "http://mvapich.cse.ohio-state.edu/" url = "http://mvapich.cse.ohio-state.edu/download/mvapich/mv2/mvapich2-2.2.tar.gz" @@ -232,32 +232,11 @@ def configure_args(self): args.extend(self.network_options) return args - @run_after('install') - def filter_compilers(self): - """Run after install to make the MPI compilers use the - compilers that Spack built the package with. - - If this isn't done, they'll have CC, CXX, F77, and FC set - to Spack's generic cc, c++, f77, and f90. We want them to - be bound to whatever compiler they were built with. - """ - bin = self.prefix.bin - mpicc = join_path(bin, 'mpicc') - mpicxx = join_path(bin, 'mpicxx') - mpif77 = join_path(bin, 'mpif77') - mpif90 = join_path(bin, 'mpif90') - mpifort = join_path(bin, 'mpifort') - - # Substitute Spack compile wrappers for the real - # underlying compiler - kwargs = {'ignore_absent': True, 'backup': False, 'string': True} - filter_file(env['CC'], self.compiler.cc, mpicc, **kwargs) - filter_file(env['CXX'], self.compiler.cxx, mpicxx, **kwargs) - filter_file(env['F77'], self.compiler.f77, mpif77, **kwargs) - filter_file(env['FC'], self.compiler.fc, mpif90, **kwargs) - filter_file(env['FC'], self.compiler.fc, mpifort, **kwargs) - - # Remove this linking flag if present - # (it turns RPATH into RUNPATH) - for wrapper in (mpicc, mpicxx, mpif77, mpif90, mpifort): - filter_file('-Wl,--enable-new-dtags', '', wrapper, **kwargs) + @property + def compiler_wrappers(self): + return [ + join_path(self.prefix.bin, 'mpicc'), + join_path(self.prefix.bin, 'mpicxx'), + join_path(self.prefix.bin, 'mpif77'), + join_path(self.prefix.bin, 'mpif90') + ] diff --git a/var/spack/repos/builtin/packages/openmpi/package.py b/var/spack/repos/builtin/packages/openmpi/package.py index 880566af2b..7362b9be7f 100644 --- a/var/spack/repos/builtin/packages/openmpi/package.py +++ b/var/spack/repos/builtin/packages/openmpi/package.py @@ -64,7 +64,7 @@ def _mxm_dir(): return None -class Openmpi(AutotoolsPackage): +class Openmpi(AutotoolsPackage, FilterCompilerWrappersPackageMixin): """The Open MPI Project is an open source Message Passing Interface implementation that is developed and maintained by a consortium of academic, research, and industry partners. Open MPI is @@ -375,43 +375,28 @@ def configure_args(self): return config_args - @run_after('install') - def filter_compilers(self): - """Run after install to make the MPI compilers use the - compilers that Spack built the package with. + @property + def compiler_wrappers(self): - If this isn't done, they'll have CC, CXX and FC set - to Spack's generic cc, c++ and f90. We want them to - be bound to whatever compiler they were built with. - """ - kwargs = {'ignore_absent': True, 'backup': False, 'string': False} - wrapper_basepath = join_path(self.prefix, 'share', 'openmpi') + basepath = join_path(self.prefix, 'share', 'openmpi') wrappers = [ - ('mpicc-vt-wrapper-data.txt', self.compiler.cc), - ('mpicc-wrapper-data.txt', self.compiler.cc), - ('ortecc-wrapper-data.txt', self.compiler.cc), - ('shmemcc-wrapper-data.txt', self.compiler.cc), - ('mpic++-vt-wrapper-data.txt', self.compiler.cxx), - ('mpic++-wrapper-data.txt', self.compiler.cxx), - ('ortec++-wrapper-data.txt', self.compiler.cxx), - ('mpifort-vt-wrapper-data.txt', self.compiler.fc), - ('mpifort-wrapper-data.txt', self.compiler.fc), - ('shmemfort-wrapper-data.txt', self.compiler.fc), - ('mpif90-vt-wrapper-data.txt', self.compiler.fc), - ('mpif90-wrapper-data.txt', self.compiler.fc), - ('mpif77-vt-wrapper-data.txt', self.compiler.f77), - ('mpif77-wrapper-data.txt', self.compiler.f77) + 'mpicc-vt-wrapper-data.txt', + 'mpicc-wrapper-data.txt', + 'ortecc-wrapper-data.txt', + 'shmemcc-wrapper-data.txt', + 'mpic++-vt-wrapper-data.txt', + 'mpic++-wrapper-data.txt', + 'ortec++-wrapper-data.txt', + 'mpifort-vt-wrapper-data.txt', + 'mpifort-wrapper-data.txt', + 'shmemfort-wrapper-data.txt', + 'mpif90-vt-wrapper-data.txt', + 'mpif90-wrapper-data.txt', + 'mpif77-vt-wrapper-data.txt', + 'mpif77-wrapper-data.txt' ] - for wrapper_name, compiler in wrappers: - wrapper = join_path(wrapper_basepath, wrapper_name) - if not os.path.islink(wrapper): - # Substitute Spack compile wrappers for the real - # underlying compiler - match = 'compiler=.*' - substitute = 'compiler={compiler}'.format(compiler=compiler) - filter_file(match, substitute, wrapper, **kwargs) - # Remove this linking flag if present - # (it turns RPATH into RUNPATH) - filter_file('-Wl,--enable-new-dtags', '', wrapper, **kwargs) + abs_wrappers = [join_path(basepath, x) for x in wrappers] + + return [x for x in abs_wrappers if not os.path.islink(x)] diff --git a/var/spack/repos/builtin/packages/r/package.py b/var/spack/repos/builtin/packages/r/package.py index ebd83ec9a8..514fcd9d8b 100644 --- a/var/spack/repos/builtin/packages/r/package.py +++ b/var/spack/repos/builtin/packages/r/package.py @@ -26,7 +26,7 @@ from spack import * -class R(AutotoolsPackage): +class R(AutotoolsPackage, FilterCompilerWrappersPackageMixin): """R is 'GNU S', a freely available language and environment for statistical computing and graphics which provides a wide variety of statistical and graphical techniques: linear and nonlinear modelling, @@ -129,25 +129,9 @@ def copy_makeconf(self): dst_makeconf = join_path(self.etcdir, 'Makeconf.spack') shutil.copy(src_makeconf, dst_makeconf) - @run_after('install') - def filter_compilers(self): - """Run after install to tell the configuration files and Makefiles - to use the compilers that Spack built the package with. - - If this isn't done, they'll have CC and CXX set to Spack's generic - cc and c++. We want them to be bound to whatever compiler - they were built with.""" - - kwargs = {'ignore_absent': True, 'backup': False, 'string': True} - - filter_file(env['CC'], self.compiler.cc, - join_path(self.etcdir, 'Makeconf'), **kwargs) - filter_file(env['CXX'], self.compiler.cxx, - join_path(self.etcdir, 'Makeconf'), **kwargs) - filter_file(env['F77'], self.compiler.f77, - join_path(self.etcdir, 'Makeconf'), **kwargs) - filter_file(env['FC'], self.compiler.fc, - join_path(self.etcdir, 'Makeconf'), **kwargs) + @property + def compiler_wrappers(self): + return [join_path(self.etcdir, 'Makeconf')] # ======================================================================== # Set up environment to make install easy for R extensions.