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
This commit is contained in:
alalazo 2017-06-09 21:07:20 +02:00 committed by Todd Gamblin
parent 4d97b540a8
commit 28e129b087
8 changed files with 144 additions and 119 deletions

View file

@ -214,6 +214,9 @@
'IntelPackage', 'IntelPackage',
] ]
from spack.mixins import FilterCompilerWrappersPackageMixin
__all__ += ['FilterCompilerWrappersPackageMixin']
from spack.version import Version, ver from spack.version import Version, ver
__all__ += ['Version', 'ver'] __all__ += ['Version', 'ver']

82
lib/spack/spack/mixins.py Normal file
View file

@ -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)

View file

@ -182,6 +182,9 @@ def _flush_callbacks(check_name):
PackageMeta.phase_fmt.format(phase_name), PackageMeta.phase_fmt.format(phase_name),
None None
) )
if phase is not None:
break
attr_dict[PackageMeta.phase_fmt.format( attr_dict[PackageMeta.phase_fmt.format(
phase_name)] = phase.copy() phase_name)] = phase.copy()
phase = attr_dict[ phase = attr_dict[
@ -618,6 +621,8 @@ def __init__(self, spec):
self.extra_args = {} self.extra_args = {}
super(PackageBase, self).__init__()
def possible_dependencies(self, transitive=True, visited=None): def possible_dependencies(self, transitive=True, visited=None):
"""Return set of possible transitive dependencies of this package. """Return set of possible transitive dependencies of this package.

View file

@ -22,12 +22,13 @@
# License along with this program; if not, write to the Free Software # License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
############################################################################## ##############################################################################
from spack import *
import shutil import shutil
import sys 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 """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 data. It supports an unlimited variety of datatypes, and is designed for
flexible and efficient I/O and for high volume and complex data. flexible and efficient I/O and for high volume and complex data.
@ -294,3 +295,11 @@ def check_install(self):
print('-' * 80) print('-' * 80)
raise RuntimeError("HDF5 install check failed") raise RuntimeError("HDF5 install check failed")
shutil.rmtree(checkdir) 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'),
]

View file

@ -26,7 +26,7 @@
import os import os
class Mpich(AutotoolsPackage): class Mpich(AutotoolsPackage, FilterCompilerWrappersPackageMixin):
"""MPICH is a high performance and widely portable implementation of """MPICH is a high performance and widely portable implementation of
the Message Passing Interface (MPI) standard.""" the Message Passing Interface (MPI) standard."""
@ -170,33 +170,11 @@ def configure_args(self):
return config_args return config_args
@run_after('install') @property
def filter_compilers(self): def compiler_wrappers(self):
"""Run after install to make the MPI compilers use the return [
compilers that Spack built the package with. join_path(self.prefix.bin, 'mpicc'),
join_path(self.prefix.bin, 'mpicxx'),
If this isn't done, they'll have CC, CXX, F77, and FC set join_path(self.prefix.bin, 'mpif77'),
to Spack's generic cc, c++, f77, and f90. We want them to join_path(self.prefix.bin, 'mpif90')
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)

View file

@ -35,7 +35,7 @@ def _process_manager_validator(values):
) )
class Mvapich2(AutotoolsPackage): class Mvapich2(AutotoolsPackage, FilterCompilerWrappersPackageMixin):
"""MVAPICH2 is an MPI implementation for Infiniband networks.""" """MVAPICH2 is an MPI implementation for Infiniband networks."""
homepage = "http://mvapich.cse.ohio-state.edu/" homepage = "http://mvapich.cse.ohio-state.edu/"
url = "http://mvapich.cse.ohio-state.edu/download/mvapich/mv2/mvapich2-2.2.tar.gz" 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) args.extend(self.network_options)
return args return args
@run_after('install') @property
def filter_compilers(self): def compiler_wrappers(self):
"""Run after install to make the MPI compilers use the return [
compilers that Spack built the package with. join_path(self.prefix.bin, 'mpicc'),
join_path(self.prefix.bin, 'mpicxx'),
If this isn't done, they'll have CC, CXX, F77, and FC set join_path(self.prefix.bin, 'mpif77'),
to Spack's generic cc, c++, f77, and f90. We want them to join_path(self.prefix.bin, 'mpif90')
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)

View file

@ -64,7 +64,7 @@ def _mxm_dir():
return None return None
class Openmpi(AutotoolsPackage): class Openmpi(AutotoolsPackage, FilterCompilerWrappersPackageMixin):
"""The Open MPI Project is an open source Message Passing Interface """The Open MPI Project is an open source Message Passing Interface
implementation that is developed and maintained by a consortium implementation that is developed and maintained by a consortium
of academic, research, and industry partners. Open MPI is of academic, research, and industry partners. Open MPI is
@ -375,43 +375,28 @@ def configure_args(self):
return config_args return config_args
@run_after('install') @property
def filter_compilers(self): def compiler_wrappers(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 and FC set basepath = join_path(self.prefix, 'share', 'openmpi')
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')
wrappers = [ wrappers = [
('mpicc-vt-wrapper-data.txt', self.compiler.cc), 'mpicc-vt-wrapper-data.txt',
('mpicc-wrapper-data.txt', self.compiler.cc), 'mpicc-wrapper-data.txt',
('ortecc-wrapper-data.txt', self.compiler.cc), 'ortecc-wrapper-data.txt',
('shmemcc-wrapper-data.txt', self.compiler.cc), 'shmemcc-wrapper-data.txt',
('mpic++-vt-wrapper-data.txt', self.compiler.cxx), 'mpic++-vt-wrapper-data.txt',
('mpic++-wrapper-data.txt', self.compiler.cxx), 'mpic++-wrapper-data.txt',
('ortec++-wrapper-data.txt', self.compiler.cxx), 'ortec++-wrapper-data.txt',
('mpifort-vt-wrapper-data.txt', self.compiler.fc), 'mpifort-vt-wrapper-data.txt',
('mpifort-wrapper-data.txt', self.compiler.fc), 'mpifort-wrapper-data.txt',
('shmemfort-wrapper-data.txt', self.compiler.fc), 'shmemfort-wrapper-data.txt',
('mpif90-vt-wrapper-data.txt', self.compiler.fc), 'mpif90-vt-wrapper-data.txt',
('mpif90-wrapper-data.txt', self.compiler.fc), 'mpif90-wrapper-data.txt',
('mpif77-vt-wrapper-data.txt', self.compiler.f77), 'mpif77-vt-wrapper-data.txt',
('mpif77-wrapper-data.txt', self.compiler.f77) 'mpif77-wrapper-data.txt'
] ]
for wrapper_name, compiler in wrappers: abs_wrappers = [join_path(basepath, x) for x in wrappers]
wrapper = join_path(wrapper_basepath, wrapper_name)
if not os.path.islink(wrapper): return [x for x in abs_wrappers if not os.path.islink(x)]
# 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)

View file

@ -26,7 +26,7 @@
from spack import * from spack import *
class R(AutotoolsPackage): class R(AutotoolsPackage, FilterCompilerWrappersPackageMixin):
"""R is 'GNU S', a freely available language and environment for """R is 'GNU S', a freely available language and environment for
statistical computing and graphics which provides a wide variety of statistical computing and graphics which provides a wide variety of
statistical and graphical techniques: linear and nonlinear modelling, statistical and graphical techniques: linear and nonlinear modelling,
@ -129,25 +129,9 @@ def copy_makeconf(self):
dst_makeconf = join_path(self.etcdir, 'Makeconf.spack') dst_makeconf = join_path(self.etcdir, 'Makeconf.spack')
shutil.copy(src_makeconf, dst_makeconf) shutil.copy(src_makeconf, dst_makeconf)
@run_after('install') @property
def filter_compilers(self): def compiler_wrappers(self):
"""Run after install to tell the configuration files and Makefiles return [join_path(self.etcdir, 'Makeconf')]
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)
# ======================================================================== # ========================================================================
# Set up environment to make install easy for R extensions. # Set up environment to make install easy for R extensions.