Merge pull request #1084 from epfl-scitas/packages/openmpi_without_fortran

OpenMPI : reverts part of #1079
This commit is contained in:
Todd Gamblin 2016-06-20 13:30:54 -07:00 committed by GitHub
commit 76d950b103
2 changed files with 85 additions and 58 deletions

View File

@ -75,20 +75,19 @@
# set_build_environment_variables and used to pass parameters to # set_build_environment_variables and used to pass parameters to
# Spack's compiler wrappers. # Spack's compiler wrappers.
# #
SPACK_ENV_PATH = 'SPACK_ENV_PATH' SPACK_ENV_PATH = 'SPACK_ENV_PATH'
SPACK_DEPENDENCIES = 'SPACK_DEPENDENCIES' SPACK_DEPENDENCIES = 'SPACK_DEPENDENCIES'
SPACK_PREFIX = 'SPACK_PREFIX' SPACK_PREFIX = 'SPACK_PREFIX'
SPACK_INSTALL = 'SPACK_INSTALL' SPACK_INSTALL = 'SPACK_INSTALL'
SPACK_DEBUG = 'SPACK_DEBUG' SPACK_DEBUG = 'SPACK_DEBUG'
SPACK_SHORT_SPEC = 'SPACK_SHORT_SPEC' SPACK_SHORT_SPEC = 'SPACK_SHORT_SPEC'
SPACK_DEBUG_LOG_DIR = 'SPACK_DEBUG_LOG_DIR' SPACK_DEBUG_LOG_DIR = 'SPACK_DEBUG_LOG_DIR'
# Platform-specific library suffix. # Platform-specific library suffix.
dso_suffix = 'dylib' if sys.platform == 'darwin' else 'so' dso_suffix = 'dylib' if sys.platform == 'darwin' else 'so'
class MakeExecutable(Executable): class MakeExecutable(Executable):
"""Special callable executable object for make so the user can """Special callable executable object for make so the user can
specify parallel or not on a per-invocation basis. Using specify parallel or not on a per-invocation basis. Using
@ -99,6 +98,7 @@ class MakeExecutable(Executable):
Note that if the SPACK_NO_PARALLEL_MAKE env var is set it overrides Note that if the SPACK_NO_PARALLEL_MAKE env var is set it overrides
everything. everything.
""" """
def __init__(self, name, jobs): def __init__(self, name, jobs):
super(MakeExecutable, self).__init__(name) super(MakeExecutable, self).__init__(name)
self.jobs = jobs self.jobs = jobs
@ -113,12 +113,13 @@ def __call__(self, *args, **kwargs):
return super(MakeExecutable, self).__call__(*args, **kwargs) return super(MakeExecutable, self).__call__(*args, **kwargs)
def load_module(mod): def load_module(mod):
"""Takes a module name and removes modules until it is possible to """Takes a module name and removes modules until it is possible to
load that module. It then loads the provided module. Depends on the load that module. It then loads the provided module. Depends on the
modulecmd implementation of modules used in cray and lmod. modulecmd implementation of modules used in cray and lmod.
""" """
#Create an executable of the module command that will output python code # Create an executable of the module command that will output python code
modulecmd = which('modulecmd') modulecmd = which('modulecmd')
modulecmd.add_default_arg('python') modulecmd.add_default_arg('python')
@ -129,11 +130,13 @@ def load_module(mod):
text = modulecmd('show', mod, output=str, error=str).split() text = modulecmd('show', mod, output=str, error=str).split()
for i, word in enumerate(text): for i, word in enumerate(text):
if word == 'conflict': if word == 'conflict':
exec(compile(modulecmd('unload', text[i+1], output=str, error=str), '<string>', 'exec')) exec(compile(modulecmd('unload', text[
i + 1], output=str, error=str), '<string>', 'exec'))
# Load the module now that there are no conflicts # Load the module now that there are no conflicts
load = modulecmd('load', mod, output=str, error=str) load = modulecmd('load', mod, output=str, error=str)
exec(compile(load, '<string>', 'exec')) exec(compile(load, '<string>', 'exec'))
def get_path_from_module(mod): def get_path_from_module(mod):
"""Inspects a TCL module for entries that indicate the absolute path """Inspects a TCL module for entries that indicate the absolute path
at which the library supported by said module can be found. at which the library supported by said module can be found.
@ -146,7 +149,7 @@ def get_path_from_module(mod):
text = modulecmd('show', mod, output=str, error=str).split('\n') text = modulecmd('show', mod, output=str, error=str).split('\n')
# If it lists its package directory, return that # If it lists its package directory, return that
for line in text: for line in text:
if line.find(mod.upper()+'_DIR') >= 0: if line.find(mod.upper() + '_DIR') >= 0:
words = line.split() words = line.split()
return words[2] return words[2]
@ -154,13 +157,13 @@ def get_path_from_module(mod):
for line in text: for line in text:
rpath = line.find('-rpath/') rpath = line.find('-rpath/')
if rpath >= 0: if rpath >= 0:
return line[rpath+6:line.find('/lib')] return line[rpath + 6:line.find('/lib')]
# If it lists a -L instruction, use that # If it lists a -L instruction, use that
for line in text: for line in text:
L = line.find('-L/') L = line.find('-L/')
if L >= 0: if L >= 0:
return line[L+2:line.find('/lib')] return line[L + 2:line.find('/lib')]
# If it sets the LD_LIBRARY_PATH or CRAY_LD_LIBRARY_PATH, use that # If it sets the LD_LIBRARY_PATH or CRAY_LD_LIBRARY_PATH, use that
for line in text: for line in text:
@ -171,32 +174,35 @@ def get_path_from_module(mod):
# Unable to find module path # Unable to find module path
return None return None
def set_compiler_environment_variables(pkg, env): def set_compiler_environment_variables(pkg, env):
assert(pkg.spec.concrete) assert(pkg.spec.concrete)
compiler = pkg.compiler compiler = pkg.compiler
flags = pkg.spec.compiler_flags flags = pkg.spec.compiler_flags
# Set compiler variables used by CMake and autotools # Set compiler variables used by CMake and autotools
assert all(key in compiler.link_paths for key in ('cc', 'cxx', 'f77', 'fc')) assert all(key in compiler.link_paths for key in (
'cc', 'cxx', 'f77', 'fc'))
# Populate an object with the list of environment modifications # Populate an object with the list of environment modifications
# and return it # and return it
# TODO : add additional kwargs for better diagnostics, like requestor, ttyout, ttyerr, etc. # TODO : add additional kwargs for better diagnostics, like requestor,
# ttyout, ttyerr, etc.
link_dir = spack.build_env_path link_dir = spack.build_env_path
env.set('CC', join_path(link_dir, compiler.link_paths['cc']))
env.set('CXX', join_path(link_dir, compiler.link_paths['cxx']))
env.set('F77', join_path(link_dir, compiler.link_paths['f77']))
env.set('FC', join_path(link_dir, compiler.link_paths['fc']))
# Set SPACK compiler variables so that our wrapper knows what to call # Set SPACK compiler variables so that our wrapper knows what to call
if compiler.cc: if compiler.cc:
env.set('SPACK_CC', compiler.cc) env.set('SPACK_CC', compiler.cc)
env.set('CC', join_path(link_dir, compiler.link_paths['cc']))
if compiler.cxx: if compiler.cxx:
env.set('SPACK_CXX', compiler.cxx) env.set('SPACK_CXX', compiler.cxx)
env.set('CXX', join_path(link_dir, compiler.link_paths['cxx']))
if compiler.f77: if compiler.f77:
env.set('SPACK_F77', compiler.f77) env.set('SPACK_F77', compiler.f77)
env.set('F77', join_path(link_dir, compiler.link_paths['f77']))
if compiler.fc: if compiler.fc:
env.set('SPACK_FC', compiler.fc) env.set('SPACK_FC', compiler.fc)
env.set('FC', join_path(link_dir, compiler.link_paths['fc']))
# Set SPACK compiler rpath flags so that our wrapper knows what to use # Set SPACK compiler rpath flags so that our wrapper knows what to use
env.set('SPACK_CC_RPATH_ARG', compiler.cc_rpath_arg) env.set('SPACK_CC_RPATH_ARG', compiler.cc_rpath_arg)
@ -233,7 +239,8 @@ def set_build_environment_variables(pkg, env):
# handled by putting one in the <build_env_path>/case-insensitive # handled by putting one in the <build_env_path>/case-insensitive
# directory. Add that to the path too. # directory. Add that to the path too.
env_paths = [] env_paths = []
for item in [spack.build_env_path, join_path(spack.build_env_path, pkg.compiler.name)]: compiler_specific = join_path(spack.build_env_path, pkg.compiler.name)
for item in [spack.build_env_path, compiler_specific]:
env_paths.append(item) env_paths.append(item)
ci = join_path(item, 'case-insensitive') ci = join_path(item, 'case-insensitive')
if os.path.isdir(ci): if os.path.isdir(ci):
@ -246,7 +253,8 @@ def set_build_environment_variables(pkg, env):
# Prefixes of all of the package's dependencies go in SPACK_DEPENDENCIES # Prefixes of all of the package's dependencies go in SPACK_DEPENDENCIES
dep_prefixes = [d.prefix for d in pkg.spec.traverse(root=False)] dep_prefixes = [d.prefix for d in pkg.spec.traverse(root=False)]
env.set_path(SPACK_DEPENDENCIES, dep_prefixes) env.set_path(SPACK_DEPENDENCIES, dep_prefixes)
env.set_path('CMAKE_PREFIX_PATH', dep_prefixes) # Add dependencies to CMAKE_PREFIX_PATH # Add dependencies to CMAKE_PREFIX_PATH
env.set_path('CMAKE_PREFIX_PATH', dep_prefixes)
# Install prefix # Install prefix
env.set(SPACK_PREFIX, pkg.prefix) env.set(SPACK_PREFIX, pkg.prefix)
@ -262,7 +270,8 @@ def set_build_environment_variables(pkg, env):
env.unset('DYLD_LIBRARY_PATH') env.unset('DYLD_LIBRARY_PATH')
# Add bin directories from dependencies to the PATH for the build. # Add bin directories from dependencies to the PATH for the build.
bin_dirs = reversed(filter(os.path.isdir, ['%s/bin' % prefix for prefix in dep_prefixes])) bin_dirs = reversed(
filter(os.path.isdir, ['%s/bin' % prefix for prefix in dep_prefixes]))
for item in bin_dirs: for item in bin_dirs:
env.prepend_path('PATH', item) env.prepend_path('PATH', item)
@ -277,8 +286,8 @@ def set_build_environment_variables(pkg, env):
for directory in ('lib', 'lib64', 'share'): for directory in ('lib', 'lib64', 'share'):
pcdir = join_path(pre, directory, 'pkgconfig') pcdir = join_path(pre, directory, 'pkgconfig')
if os.path.isdir(pcdir): if os.path.isdir(pcdir):
#pkg_config_dirs.append(pcdir) # pkg_config_dirs.append(pcdir)
env.prepend_path('PKG_CONFIG_PATH',pcdir) env.prepend_path('PKG_CONFIG_PATH', pcdir)
if pkg.spec.architecture.target.module_name: if pkg.spec.architecture.target.module_name:
load_module(pkg.spec.architecture.target.module_name) load_module(pkg.spec.architecture.target.module_name)
@ -301,7 +310,7 @@ def set_module_variables_for_package(pkg, module):
m.make_jobs = jobs m.make_jobs = jobs
# TODO: make these build deps that can be installed if not found. # TODO: make these build deps that can be installed if not found.
m.make = MakeExecutable('make', jobs) m.make = MakeExecutable('make', jobs)
m.gmake = MakeExecutable('gmake', jobs) m.gmake = MakeExecutable('gmake', jobs)
# easy shortcut to os.environ # easy shortcut to os.environ
@ -325,33 +334,34 @@ def set_module_variables_for_package(pkg, module):
# Set up CMake rpath # Set up CMake rpath
m.std_cmake_args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE') m.std_cmake_args.append('-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE')
m.std_cmake_args.append('-DCMAKE_INSTALL_RPATH=%s' % ":".join(get_rpaths(pkg))) m.std_cmake_args.append('-DCMAKE_INSTALL_RPATH=%s' %
":".join(get_rpaths(pkg)))
# Put spack compiler paths in module scope. # Put spack compiler paths in module scope.
link_dir = spack.build_env_path link_dir = spack.build_env_path
m.spack_cc = join_path(link_dir, pkg.compiler.link_paths['cc']) m.spack_cc = join_path(link_dir, pkg.compiler.link_paths['cc'])
m.spack_cxx = join_path(link_dir, pkg.compiler.link_paths['cxx']) m.spack_cxx = join_path(link_dir, pkg.compiler.link_paths['cxx'])
m.spack_f77 = join_path(link_dir, pkg.compiler.link_paths['f77']) m.spack_f77 = join_path(link_dir, pkg.compiler.link_paths['f77'])
m.spack_fc = join_path(link_dir, pkg.compiler.link_paths['fc']) m.spack_fc = join_path(link_dir, pkg.compiler.link_paths['fc'])
# Emulate some shell commands for convenience # Emulate some shell commands for convenience
m.pwd = os.getcwd m.pwd = os.getcwd
m.cd = os.chdir m.cd = os.chdir
m.mkdir = os.mkdir m.mkdir = os.mkdir
m.makedirs = os.makedirs m.makedirs = os.makedirs
m.remove = os.remove m.remove = os.remove
m.removedirs = os.removedirs m.removedirs = os.removedirs
m.symlink = os.symlink m.symlink = os.symlink
m.mkdirp = mkdirp m.mkdirp = mkdirp
m.install = install m.install = install
m.install_tree = install_tree m.install_tree = install_tree
m.rmtree = shutil.rmtree m.rmtree = shutil.rmtree
m.move = shutil.move m.move = shutil.move
# Useful directories within the prefix are encapsulated in # Useful directories within the prefix are encapsulated in
# a Prefix object. # a Prefix object.
m.prefix = pkg.prefix m.prefix = pkg.prefix
# Platform-specific library suffix. # Platform-specific library suffix.
m.dso_suffix = dso_suffix m.dso_suffix = dso_suffix
@ -372,13 +382,15 @@ def get_rpaths(pkg):
def parent_class_modules(cls): def parent_class_modules(cls):
"""Get list of super class modules that are all descend from spack.Package""" """
Get list of super class modules that are all descend from spack.Package
"""
if not issubclass(cls, spack.Package) or issubclass(spack.Package, cls): if not issubclass(cls, spack.Package) or issubclass(spack.Package, cls):
return [] return []
result = [] result = []
module = sys.modules.get(cls.__module__) module = sys.modules.get(cls.__module__)
if module: if module:
result = [ module ] result = [module]
for c in cls.__bases__: for c in cls.__bases__:
result.extend(parent_class_modules(c)) result.extend(parent_class_modules(c))
return result return result
@ -391,10 +403,11 @@ def load_external_modules(pkg):
if dep.external_module: if dep.external_module:
load_module(dep.external_module) load_module(dep.external_module)
def setup_package(pkg): def setup_package(pkg):
"""Execute all environment setup routines.""" """Execute all environment setup routines."""
spack_env = EnvironmentModifications() spack_env = EnvironmentModifications()
run_env = EnvironmentModifications() run_env = EnvironmentModifications()
# Before proceeding, ensure that specs and packages are consistent # Before proceeding, ensure that specs and packages are consistent
# #
@ -410,7 +423,8 @@ def setup_package(pkg):
# throwaway environment, but it is kind of dirty. # throwaway environment, but it is kind of dirty.
# #
# TODO: Think about how to avoid this fix and do something cleaner. # TODO: Think about how to avoid this fix and do something cleaner.
for s in pkg.spec.traverse(): s.package.spec = s for s in pkg.spec.traverse():
s.package.spec = s
set_compiler_environment_variables(pkg, spack_env) set_compiler_environment_variables(pkg, spack_env)
set_build_environment_variables(pkg, spack_env) set_build_environment_variables(pkg, spack_env)
@ -498,7 +512,9 @@ def child_fun():
# message. Just make the parent exit with an error code. # message. Just make the parent exit with an error code.
pid, returncode = os.waitpid(pid, 0) pid, returncode = os.waitpid(pid, 0)
if returncode != 0: if returncode != 0:
raise InstallError("Installation process had nonzero exit code.".format(str(returncode))) message = "Installation process had nonzero exit code : {code}"
strcode = str(returncode)
raise InstallError(message.format(code=strcode))
class InstallError(spack.error.SpackError): class InstallError(spack.error.SpackError):

View File

@ -24,6 +24,8 @@
############################################################################## ##############################################################################
import os import os
import llnl.util.tty as tty
from spack import * from spack import *
@ -118,6 +120,21 @@ def setup_dependent_package(self, module, dep_spec):
self.spec.mpifc = join_path(self.prefix.bin, 'mpif90') self.spec.mpifc = join_path(self.prefix.bin, 'mpif90')
self.spec.mpif77 = join_path(self.prefix.bin, 'mpif77') self.spec.mpif77 = join_path(self.prefix.bin, 'mpif77')
def setup_environment(self, spack_env, run_env):
# As of 06/2016 there is no mechanism to specify that packages which
# depends on MPI need C or/and Fortran implementation. For now
# require both.
if (self.compiler.f77 is None) or (self.compiler.fc is None):
tty.warn('OpenMPI : FORTRAN compiler not found')
tty.warn('OpenMPI : FORTRAN bindings will be disabled')
spack_env.unset('FC')
spack_env.unset('F77')
# Setting an attribute here and using it in the 'install'
# method is needed to ensure tty.warn is actually displayed
# to user and not redirected to spack-build.out
self.config_extra = ['--enable-mpi-fortran=none',
'--disable-oshmem-fortran']
@property @property
def verbs(self): def verbs(self):
# Up through version 1.6, this option was previously named # Up through version 1.6, this option was previously named
@ -129,17 +146,14 @@ def verbs(self):
return 'verbs' return 'verbs'
def install(self, spec, prefix): def install(self, spec, prefix):
# As of 06/2016 there is no mechanism to specify that packages which
# depends on MPI need C or/and Fortran implementation. For now
# require both.
if (self.compiler.f77 is None) or (self.compiler.fc is None):
raise InstallError('OpenMPI requires both C and Fortran ',
'compilers!')
config_args = ["--prefix=%s" % prefix, config_args = ["--prefix=%s" % prefix,
"--with-hwloc=%s" % spec['hwloc'].prefix, "--with-hwloc=%s" % spec['hwloc'].prefix,
"--enable-shared", "--enable-shared",
"--enable-static"] "--enable-static"]
if getattr(self, 'config_extra', None) is not None:
config_args.extend(self.config_extra)
# Variant based arguments # Variant based arguments
config_args.extend([ config_args.extend([
# Schedulers # Schedulers
@ -170,9 +184,6 @@ def install(self, spec, prefix):
if self.version == ver("1.6.5") and '+lanl' in spec: if self.version == ver("1.6.5") and '+lanl' in spec:
config_args.append("--with-platform=contrib/platform/lanl/tlcc2/optimized-nopanasas") # NOQA: ignore=E501 config_args.append("--with-platform=contrib/platform/lanl/tlcc2/optimized-nopanasas") # NOQA: ignore=E501
if not self.compiler.f77 and not self.compiler.fc:
config_args.append("--enable-mpi-fortran=no")
configure(*config_args) configure(*config_args)
make() make()
make("install") make("install")