tclmodules : added hooks to process EnvironmentModifications objects

This commit is contained in:
alalazo 2016-03-16 15:19:13 +01:00
parent b45ec3f04e
commit 597727f8be
3 changed files with 133 additions and 50 deletions

View File

@ -47,18 +47,18 @@
__all__ = ['EnvModule', 'Dotkit', 'TclModule']
import os
import os.path
import re
import textwrap
import shutil
import textwrap
from glob import glob
import llnl.util.tty as tty
import spack
from spack.environment import *
from llnl.util.filesystem import join_path, mkdirp
import spack
"""Registry of all types of modules. Entries created by EnvModule's
metaclass."""
# Registry of all types of modules. Entries created by EnvModule's metaclass
module_types = {}
@ -79,6 +79,32 @@ def print_help():
"")
class PathInspector(object):
dirname2varname = {
'bin': ('PATH',),
'man': ('MANPATH',),
'lib': ('LIBRARY_PATH', 'LD_LIBRARY_PATH'),
'lib64': ('LIBRARY_PATH', 'LD_LIBRARY_PATH'),
'include': ('CPATH',),
'pkgconfig': ('PKG_CONFIG_PATH',)
}
def __call__(self, env, directory, names):
for name in names:
variables = PathInspector.dirname2varname.get(name, None)
if variables is None:
continue
absolute_path = join_path(os.path.abspath(directory), name)
for variable in variables:
env.prepend_path(variable, absolute_path)
def inspect_path(path):
env, inspector = EnvironmentModifications(), PathInspector()
os.path.walk(path, inspector, env)
return env
class EnvModule(object):
name = 'env_module'
@ -88,21 +114,27 @@ def __init__(cls, name, bases, dict):
if cls.name != 'env_module':
module_types[cls.name] = cls
def __init__(self, spec=None):
# category in the modules system
# TODO: come up with smarter category names.
self.category = "spack"
# Descriptions for the module system's UI
self.short_description = ""
self.long_description = ""
# dict pathname -> list of directories to be prepended to in
# the module file.
self._paths = None
self.spec = spec
self.pkg = spec.package # Just stored for convenience
# short description default is just the package + version
# packages can provide this optional attribute
self.short_description = spec.format("$_ $@")
if hasattr(self.pkg, 'short_description'):
self.short_description = self.pkg.short_description
# long description is the docstring with reduced whitespace.
self.long_description = None
if self.spec.package.__doc__:
self.long_description = re.sub(r'\s+', ' ', self.spec.package.__doc__)
@property
def paths(self):
@ -130,26 +162,19 @@ def add_path(path_name, directory):
add_path(var, directory)
# Add python path unless it's an actual python installation
# TODO: is there a better way to do this?
# TODO : is there a better way to do this?
# FIXME : add PYTHONPATH to every python package
if self.spec.name != 'python':
site_packages = glob(join_path(self.spec.prefix.lib, "python*/site-packages"))
if site_packages:
add_path('PYTHONPATH', site_packages[0])
# FIXME : Same for GEM_PATH
if self.spec.package.extends(spack.spec.Spec('ruby')):
add_path('GEM_PATH', self.spec.prefix)
# short description is just the package + version
# TODO: maybe packages can optionally provide it.
self.short_description = self.spec.format("$_ $@")
# long description is the docstring with reduced whitespace.
if self.spec.package.__doc__:
self.long_description = re.sub(r'\s+', ' ', self.spec.package.__doc__)
return self._paths
def write(self):
"""Write out a module file for this object."""
module_dir = os.path.dirname(self.file_name)
@ -160,9 +185,18 @@ def write(self):
if not self.paths:
return
with open(self.file_name, 'w') as f:
self._write(f)
# Construct the changes that needs to be done on the environment for
env = inspect_path(self.spec.prefix)
# FIXME : move the logic to inspection
env.prepend_path('CMAKE_PREFIX_PATH', self.spec.prefix)
# FIXME : decide how to distinguish between calls done in the installation and elsewhere
env.extend(self.spec.package.environment_modifications(None))
# site_specific = ...`
if not env:
return
with open(self.file_name, 'w') as f:
self._write(f, env)
def _write(self, stream):
"""To be implemented by subclasses."""
@ -175,14 +209,12 @@ def file_name(self):
where this module lives."""
raise NotImplementedError()
@property
def use_name(self):
"""Subclasses should implement this to return the name the
module command uses to refer to the package."""
raise NotImplementedError()
def remove(self):
mod_file = self.file_name
if os.path.exists(mod_file):
@ -205,7 +237,7 @@ def use_name(self):
self.spec.compiler.version,
self.spec.dag_hash())
def _write(self, dk_file):
def _write(self, dk_file, env):
# Category
if self.category:
dk_file.write('#c %s\n' % self.category)
@ -231,6 +263,10 @@ def _write(self, dk_file):
class TclModule(EnvModule):
name = 'tcl'
path = join_path(spack.share_path, "modules")
formats = {
PrependPath: 'prepend-path {0.name} \"{0.path}\"\n',
SetEnv: 'setenv {0.name} \"{0.value}\"\n'
}
@property
def file_name(self):
@ -244,25 +280,56 @@ def use_name(self):
self.spec.compiler.version,
self.spec.dag_hash())
def process_environment_command(self, env):
for command in env:
# FIXME : how should we handle errors here?
yield self.formats[type(command)].format(command)
def _write(self, m_file):
# TODO: cateogry?
m_file.write('#%Module1.0\n')
def _write(self, module_file, env):
"""
Writes a TCL module file for this package
Args:
module_file: module file stream
env: list of environment modifications to be written in the module file
"""
# TCL Modulefile header
module_file.write('#%Module1.0\n')
# TODO : category ?
# Short description
if self.short_description:
m_file.write('module-whatis \"%s\"\n\n' % self.short_description)
module_file.write('module-whatis \"%s\"\n\n' % self.short_description)
# Long description
if self.long_description:
m_file.write('proc ModulesHelp { } {\n')
module_file.write('proc ModulesHelp { } {\n')
doc = re.sub(r'"', '\"', self.long_description)
m_file.write("puts stderr \"%s\"\n" % doc)
m_file.write('}\n\n')
module_file.write("puts stderr \"%s\"\n" % doc)
module_file.write('}\n\n')
# Path alterations
for var, dirs in self.paths.items():
for directory in dirs:
m_file.write("prepend-path %s \"%s\"\n" % (var, directory))
# Environment modifications
for line in self.process_environment_command(env):
module_file.write(line)
m_file.write("prepend-path CMAKE_PREFIX_PATH \"%s\"\n" % self.spec.prefix)
# FIXME : REMOVE
# def _write(self, m_file):
# # TODO: cateogry?
# m_file.write('#%Module1.0\n')
#
# # Short description
# if self.short_description:
# m_file.write('module-whatis \"%s\"\n\n' % self.short_description)
#
# # Long description
# if self.long_description:
# m_file.write('proc ModulesHelp { } {\n')
# doc = re.sub(r'"', '\"', self.long_description)
# m_file.write("puts stderr \"%s\"\n" % doc)
# m_file.write('}\n\n')
#
# # Path alterations
# for var, dirs in self.paths.items():
# for directory in dirs:
# m_file.write("prepend-path %s \"%s\"\n" % (var, directory))
#
# m_file.write("prepend-path CMAKE_PREFIX_PATH \"%s\"\n" % self.spec.prefix)

View File

@ -25,6 +25,7 @@
from spack import *
import os
class Mpich(Package):
"""MPICH is a high performance and widely portable implementation of
the Message Passing Interface (MPI) standard."""
@ -48,11 +49,25 @@ class Mpich(Package):
def environment_modifications(self, dependent_spec):
env = super(Mpich, self).environment_modifications(dependent_spec)
env.set_env('MPICH_CC', os.environ['CC'])
env.set_env('MPICH_CXX', os.environ['CXX'])
env.set_env('MPICH_F77', os.environ['F77'])
env.set_env('MPICH_F90', os.environ['FC'])
env.set_env('MPICH_FC', os.environ['FC'])
if dependent_spec is None:
# We are not using compiler wrappers
cc = self.compiler.cc
cxx = self.compiler.cxx
f77 = self.compiler.f77
f90 = fc = self.compiler.fc
else:
# Spack compiler wrappers
cc = os.environ['CC']
cxx = os.environ['CXX']
f77 = os.environ['F77']
f90 = fc = os.environ['FC']
env.set_env('MPICH_CC', cc)
env.set_env('MPICH_CXX', cxx)
env.set_env('MPICH_F77', f77)
env.set_env('MPICH_F90', f90)
env.set_env('MPICH_FC', fc)
return env
def module_modifications(self, module, spec, dep_spec):

View File

@ -91,6 +91,7 @@ def site_packages_dir(self):
def environment_modifications(self, extension_spec):
env = super(Python, self).environment_modifications(extension_spec)
if extension_spec is not None:
# Set PYTHONPATH to include site-packages dir for the
# extension and any other python extensions it depends on.
python_paths = []