summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/build_environment.py132
-rw-r--r--lib/spack/spack/environment.py198
-rw-r--r--lib/spack/spack/package.py84
-rw-r--r--lib/spack/spack/test/__init__.py3
-rw-r--r--lib/spack/spack/test/database.py1
-rw-r--r--lib/spack/spack/test/environment.py65
-rw-r--r--lib/spack/spack/util/environment.py8
-rw-r--r--var/spack/repos/builtin/packages/mpich/package.py18
-rw-r--r--var/spack/repos/builtin/packages/mvapich2/package.py2
-rw-r--r--var/spack/repos/builtin/packages/netlib-scalapack/package.py5
-rw-r--r--var/spack/repos/builtin/packages/openmpi/package.py14
-rw-r--r--var/spack/repos/builtin/packages/python/package.py20
-rw-r--r--var/spack/repos/builtin/packages/qt/package.py9
-rw-r--r--var/spack/repos/builtin/packages/ruby/package.py27
14 files changed, 434 insertions, 152 deletions
diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py
index 87fc310b5a..e86a7c413a 100644
--- a/lib/spack/spack/build_environment.py
+++ b/lib/spack/spack/build_environment.py
@@ -3,7 +3,7 @@ This module contains all routines related to setting up the package
build environment. All of this is set up by package.py just before
install() is called.
-There are two parts to the bulid environment:
+There are two parts to the build environment:
1. Python build environment (i.e. install() method)
@@ -13,7 +13,7 @@ There are two parts to the bulid environment:
the package's module scope. Ths allows package writers to call
them all directly in Package.install() without writing 'self.'
everywhere. No, this isn't Pythonic. Yes, it makes the code more
- readable and more like the shell script from whcih someone is
+ readable and more like the shell script from which someone is
likely porting.
2. Build execution environment
@@ -27,17 +27,17 @@ There are two parts to the bulid environment:
Skimming this module is a nice way to get acquainted with the types of
calls you can make from within the install() function.
"""
-import os
-import sys
-import shutil
import multiprocessing
+import os
import platform
-from llnl.util.filesystem import *
+import shutil
+import sys
import spack
-import spack.compilers as compilers
-from spack.util.executable import Executable, which
+from llnl.util.filesystem import *
+from spack.environment import EnvironmentModifications, apply_environment_modifications, concatenate_paths
from spack.util.environment import *
+from spack.util.executable import Executable, which
#
# This can be set by the user to globally disable parallel builds.
@@ -84,84 +84,88 @@ class MakeExecutable(Executable):
def set_compiler_environment_variables(pkg):
- assert(pkg.spec.concrete)
- compiler = pkg.compiler
-
+ assert pkg.spec.concrete
# Set compiler variables used by CMake and autotools
- assert all(key in pkg.compiler.link_paths
- for key in ('cc', 'cxx', 'f77', 'fc'))
+ assert all(key in pkg.compiler.link_paths for key in ('cc', 'cxx', 'f77', 'fc'))
+ # Populate an object with the list of environment modifications
+ # and return it
+ # TODO : add additional kwargs for better diagnostics, like requestor, ttyout, ttyerr, etc.
+ env = EnvironmentModifications()
link_dir = spack.build_env_path
- os.environ['CC'] = join_path(link_dir, pkg.compiler.link_paths['cc'])
- os.environ['CXX'] = join_path(link_dir, pkg.compiler.link_paths['cxx'])
- os.environ['F77'] = join_path(link_dir, pkg.compiler.link_paths['f77'])
- os.environ['FC'] = join_path(link_dir, pkg.compiler.link_paths['fc'])
-
+ env.set_env('CC', join_path(link_dir, pkg.compiler.link_paths['cc']))
+ env.set_env('CXX', join_path(link_dir, pkg.compiler.link_paths['cxx']))
+ env.set_env('F77', join_path(link_dir, pkg.compiler.link_paths['f77']))
+ env.set_env('FC', join_path(link_dir, pkg.compiler.link_paths['fc']))
# Set SPACK compiler variables so that our wrapper knows what to call
+ compiler = pkg.compiler
if compiler.cc:
- os.environ['SPACK_CC'] = compiler.cc
+ env.set_env('SPACK_CC', compiler.cc)
if compiler.cxx:
- os.environ['SPACK_CXX'] = compiler.cxx
+ env.set_env('SPACK_CXX', compiler.cxx)
if compiler.f77:
- os.environ['SPACK_F77'] = compiler.f77
+ env.set_env('SPACK_F77', compiler.f77)
if compiler.fc:
- os.environ['SPACK_FC'] = compiler.fc
+ env.set_env('SPACK_FC', compiler.fc)
- os.environ['SPACK_COMPILER_SPEC'] = str(pkg.spec.compiler)
+ env.set_env('SPACK_COMPILER_SPEC', str(pkg.spec.compiler))
+ return env
def set_build_environment_variables(pkg):
- """This ensures a clean install environment when we build packages.
+ """
+ This ensures a clean install environment when we build packages
"""
# Add spack build environment path with compiler wrappers first in
# the path. We add both spack.env_path, which includes default
# wrappers (cc, c++, f77, f90), AND a subdirectory containing
# compiler-specific symlinks. The latter ensures that builds that
# are sensitive to the *name* of the compiler see the right name
- # when we're building wtih the wrappers.
+ # when we're building with the wrappers.
#
# Conflicts on case-insensitive systems (like "CC" and "cc") are
# handled by putting one in the <build_env_path>/case-insensitive
# directory. Add that to the path too.
env_paths = []
- def add_env_path(path):
- env_paths.append(path)
- ci = join_path(path, 'case-insensitive')
- if os.path.isdir(ci): env_paths.append(ci)
- add_env_path(spack.build_env_path)
- add_env_path(join_path(spack.build_env_path, pkg.compiler.name))
-
- path_put_first("PATH", env_paths)
- path_set(SPACK_ENV_PATH, env_paths)
-
- # Prefixes of all of the package's dependencies go in
- # SPACK_DEPENDENCIES
+ for item in [spack.build_env_path, join_path(spack.build_env_path, pkg.compiler.name)]:
+ env_paths.append(item)
+ ci = join_path(item, 'case-insensitive')
+ if os.path.isdir(ci):
+ env_paths.append(ci)
+
+ env = EnvironmentModifications()
+ for item in reversed(env_paths):
+ env.prepend_path('PATH', item)
+ env.set_env(SPACK_ENV_PATH, concatenate_paths(env_paths))
+
+ # Prefixes of all of the package's dependencies go in SPACK_DEPENDENCIES
dep_prefixes = [d.prefix for d in pkg.spec.traverse(root=False)]
- path_set(SPACK_DEPENDENCIES, dep_prefixes)
+ env.set_env(SPACK_DEPENDENCIES, concatenate_paths(dep_prefixes))
+ env.set_env('CMAKE_PREFIX_PATH', concatenate_paths(dep_prefixes)) # Add dependencies to CMAKE_PREFIX_PATH
# Install prefix
- os.environ[SPACK_PREFIX] = pkg.prefix
+ env.set_env(SPACK_PREFIX, pkg.prefix)
# Install root prefix
- os.environ[SPACK_INSTALL] = spack.install_path
+ env.set_env(SPACK_INSTALL, spack.install_path)
# Remove these vars from the environment during build because they
# can affect how some packages find libraries. We want to make
# sure that builds never pull in unintended external dependencies.
- pop_keys(os.environ, "LD_LIBRARY_PATH", "LD_RUN_PATH", "DYLD_LIBRARY_PATH")
+ env.unset_env('LD_LIBRARY_PATH')
+ env.unset_env('LD_RUN_PATH')
+ env.unset_env('DYLD_LIBRARY_PATH')
# Add bin directories from dependencies to the PATH for the build.
- bin_dirs = ['%s/bin' % prefix for prefix in dep_prefixes]
- path_put_first('PATH', [bin for bin in bin_dirs if os.path.isdir(bin)])
+ bin_dirs = reversed(filter(os.path.isdir, ['%s/bin' % prefix for prefix in dep_prefixes]))
+ for item in bin_dirs:
+ env.prepend_path('PATH', item)
# Working directory for the spack command itself, for debug logs.
if spack.debug:
- os.environ[SPACK_DEBUG] = "TRUE"
- os.environ[SPACK_SHORT_SPEC] = pkg.spec.short_spec
- os.environ[SPACK_DEBUG_LOG_DIR] = spack.spack_working_dir
-
- # Add dependencies to CMAKE_PREFIX_PATH
- path_set("CMAKE_PREFIX_PATH", dep_prefixes)
+ env.set_env(SPACK_DEBUG, 'TRUE')
+ env.set_env(SPACK_SHORT_SPEC, pkg.spec.short_spec)
+ env.set_env(SPACK_DEBUG_LOG_DIR, spack.spack_working_dir)
# Add any pkgconfig directories to PKG_CONFIG_PATH
pkg_config_dirs = []
@@ -170,7 +174,9 @@ def set_build_environment_variables(pkg):
pcdir = join_path(p, libdir, 'pkgconfig')
if os.path.isdir(pcdir):
pkg_config_dirs.append(pcdir)
- path_set("PKG_CONFIG_PATH", pkg_config_dirs)
+ env.set_env('PKG_CONFIG_PATH', concatenate_paths(pkg_config_dirs))
+
+ return env
def set_module_variables_for_package(pkg, m):
@@ -264,9 +270,9 @@ def parent_class_modules(cls):
def setup_package(pkg):
"""Execute all environment setup routines."""
- set_compiler_environment_variables(pkg)
- set_build_environment_variables(pkg)
-
+ env = EnvironmentModifications()
+ env.extend(set_compiler_environment_variables(pkg))
+ env.extend(set_build_environment_variables(pkg))
# If a user makes their own package repo, e.g.
# spack.repos.mystuff.libelf.Libelf, and they inherit from
# an existing class like spack.repos.original.libelf.Libelf,
@@ -278,8 +284,9 @@ def setup_package(pkg):
# Allow dependencies to set up environment as well.
for dep_spec in pkg.spec.traverse(root=False):
- dep_spec.package.setup_dependent_environment(
- pkg.module, dep_spec, pkg.spec)
+ dep_spec.package.module_modifications(pkg.module, dep_spec, pkg.spec)
+ env.extend(dep_spec.package.environment_modifications(pkg.spec))
+ apply_environment_modifications(env)
def fork(pkg, function):
@@ -296,23 +303,23 @@ def fork(pkg, function):
# do stuff
build_env.fork(pkg, child_fun)
- Forked processes are run with the build environemnt set up by
+ Forked processes are run with the build environment set up by
spack.build_environment. This allows package authors to have
- full control over the environment, etc. without offecting
+ full control over the environment, etc. without affecting
other builds that might be executed in the same spack call.
- If something goes wrong, the child process is expected toprint
+ If something goes wrong, the child process is expected to print
the error and the parent process will exit with error as
well. If things go well, the child exits and the parent
carries on.
"""
try:
pid = os.fork()
- except OSError, e:
+ except OSError as e:
raise InstallError("Unable to fork build process: %s" % e)
if pid == 0:
- # Give the child process the package's build environemnt.
+ # Give the child process the package's build environment.
setup_package(pkg)
try:
@@ -323,7 +330,7 @@ def fork(pkg, function):
# which interferes with unit tests.
os._exit(0)
- except spack.error.SpackError, e:
+ except spack.error.SpackError as e:
e.die()
except:
@@ -338,8 +345,7 @@ def fork(pkg, function):
# message. Just make the parent exit with an error code.
pid, returncode = os.waitpid(pid, 0)
if returncode != 0:
- raise InstallError("Installation process had nonzero exit code."
- .format(str(returncode)))
+ raise InstallError("Installation process had nonzero exit code.".format(str(returncode)))
class InstallError(spack.error.SpackError):
diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py
new file mode 100644
index 0000000000..5a68e10c99
--- /dev/null
+++ b/lib/spack/spack/environment.py
@@ -0,0 +1,198 @@
+import os
+import os.path
+import collections
+
+
+class AttributeHolder(object):
+ """
+ Policy that permits to store any kind of attribute on self. The attributes must be passed as key/value pairs
+ during the initialization of the instance.
+ """
+ def __init__(self, **kwargs):
+ for key, value in kwargs.items():
+ setattr(self, key, value)
+
+
+class SetEnv(AttributeHolder):
+ def __init__(self, name, value, **kwargs):
+ super(SetEnv, self).__init__(**kwargs)
+ self.name = name
+ self.value = value
+
+ def execute(self):
+ os.environ[self.name] = str(self.value)
+
+
+class UnsetEnv(AttributeHolder):
+ def __init__(self, name, **kwargs):
+ super(UnsetEnv, self).__init__(**kwargs)
+ self.name = name
+
+ def execute(self):
+ os.environ.pop(self.name, None) # Avoid throwing if the variable was not set
+
+
+class AppendPath(AttributeHolder):
+ def __init__(self, name, path, **kwargs):
+ super(AppendPath, self).__init__(**kwargs)
+ self.name = name
+ self.path = path
+
+ def execute(self):
+ environment_value = os.environ.get(self.name, '')
+ directories = environment_value.split(':') if environment_value else []
+ # TODO : Check if this is a valid directory name
+ directories.append(os.path.normpath(self.path))
+ os.environ[self.name] = ':'.join(directories)
+
+
+class PrependPath(AttributeHolder):
+ def __init__(self, name, path, **kwargs):
+ super(PrependPath, self).__init__(**kwargs)
+ self.name = name
+ self.path = path
+
+ def execute(self):
+ environment_value = os.environ.get(self.name, '')
+ directories = environment_value.split(':') if environment_value else []
+ # TODO : Check if this is a valid directory name
+ directories = [os.path.normpath(self.path)] + directories
+ os.environ[self.name] = ':'.join(directories)
+
+
+class RemovePath(AttributeHolder):
+ def __init__(self, name, path, **kwargs):
+ super(RemovePath, self).__init__(**kwargs)
+ self.name = name
+ self.path = path
+
+ def execute(self):
+ environment_value = os.environ.get(self.name, '')
+ directories = environment_value.split(':') if environment_value else []
+ directories = [os.path.normpath(x) for x in directories if x != os.path.normpath(self.path)]
+ os.environ[self.name] = ':'.join(directories)
+
+
+class EnvironmentModifications(object):
+ """
+ Keeps track of requests to modify the current environment.
+ """
+
+ def __init__(self, other=None):
+ """
+ Initializes a new instance, copying commands from other if it is not None
+
+ Args:
+ other: another instance of EnvironmentModifications from which (optional)
+ """
+ self.env_modifications = []
+ if other is not None:
+ self.extend(other)
+
+ def __iter__(self):
+ return iter(self.env_modifications)
+
+ def __len__(self):
+ return len(self.env_modifications)
+
+ def extend(self, other):
+ self._check_other(other)
+ self.env_modifications.extend(other.env_modifications)
+
+ @staticmethod
+ def _check_other(other):
+ if not isinstance(other, EnvironmentModifications):
+ raise TypeError('other must be an instance of EnvironmentModifications')
+
+ def set_env(self, name, value, **kwargs):
+ """
+ Stores in the current object a request to set an environment variable
+
+ Args:
+ name: name of the environment variable to be set
+ value: value of the environment variable
+ """
+ item = SetEnv(name, value, **kwargs)
+ self.env_modifications.append(item)
+
+ def unset_env(self, name, **kwargs):
+ """
+ Stores in the current object a request to unset an environment variable
+
+ Args:
+ name: name of the environment variable to be set
+ """
+ item = UnsetEnv(name, **kwargs)
+ self.env_modifications.append(item)
+
+ def append_path(self, name, path, **kwargs):
+ """
+ Stores in the current object a request to append a path to a path list
+
+ Args:
+ name: name of the path list in the environment
+ path: path to be appended
+ """
+ item = AppendPath(name, path, **kwargs)
+ self.env_modifications.append(item)
+
+ def prepend_path(self, name, path, **kwargs):
+ """
+ Same as `append_path`, but the path is pre-pended
+
+ Args:
+ name: name of the path list in the environment
+ path: path to be pre-pended
+ """
+ item = PrependPath(name, path, **kwargs)
+ self.env_modifications.append(item)
+
+ def remove_path(self, name, path, **kwargs):
+ """
+ Stores in the current object a request to remove a path from a path list
+
+ Args:
+ name: name of the path list in the environment
+ path: path to be removed
+ """
+ item = RemovePath(name, path, **kwargs)
+ self.env_modifications.append(item)
+
+
+def concatenate_paths(paths):
+ """
+ Concatenates an iterable of paths into a column separated string
+
+ Args:
+ paths: iterable of paths
+
+ Returns:
+ column separated string
+ """
+ return ':'.join(str(item) for item in paths)
+
+
+def validate_environment_modifications(env):
+ modifications = collections.defaultdict(list)
+ for item in env:
+ modifications[item.name].append(item)
+ # TODO : once we organized the modifications into a dictionary that maps an environment variable
+ # TODO : to a list of action to be done on it, we may easily spot inconsistencies and warn the user if
+ # TODO : something suspicious is happening
+ return modifications
+
+
+def apply_environment_modifications(env):
+ """
+ Modifies the current environment according to the request in env
+
+ Args:
+ env: object storing modifications to the environment
+ """
+ modifications = validate_environment_modifications(env)
+
+ # Cycle over the environment variables that will be modified
+ for variable, actions in modifications.items():
+ # Execute all the actions in the order they were issued
+ for x in actions:
+ x.execute()
diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py
index 696adaf896..042833964a 100644
--- a/lib/spack/spack/package.py
+++ b/lib/spack/spack/package.py
@@ -34,40 +34,34 @@ rundown on spack and how it differs from homebrew, look at the
README.
"""
import os
-import errno
import re
-import shutil
-import time
-import itertools
-import subprocess
-import platform as py_platform
-import multiprocessing
-from urlparse import urlparse, urljoin
import textwrap
-from StringIO import StringIO
+import time
import llnl.util.tty as tty
-from llnl.util.tty.log import log_output
-from llnl.util.link_tree import LinkTree
-from llnl.util.filesystem import *
-from llnl.util.lang import *
-
import spack
-import spack.error
+import spack.build_environment
import spack.compilers
-import spack.mirror
-import spack.hooks
import spack.directives
+import spack.error
+import spack.fetch_strategy as fs
+import spack.hooks
+import spack.mirror
import spack.repository
-import spack.build_environment
import spack.url
import spack.util.web
-import spack.fetch_strategy as fs
-from spack.version import *
+from StringIO import StringIO
+from llnl.util.filesystem import *
+from llnl.util.lang import *
+from llnl.util.link_tree import LinkTree
+from llnl.util.tty.log import log_output
+from spack.environment import EnvironmentModifications
from spack.stage import Stage, ResourceStage, StageComposite
-from spack.util.compression import allowed_archive, extension
-from spack.util.executable import ProcessError
+from spack.util.compression import allowed_archive
from spack.util.environment import dump_environment
+from spack.util.executable import ProcessError
+from spack.version import *
+from urlparse import urlparse
"""Allowed URL schemes for spack packages."""
_ALLOWED_URL_SCHEMES = ["http", "https", "ftp", "file", "git"]
@@ -983,28 +977,42 @@ class Package(object):
fromlist=[self.__class__.__name__])
- def setup_dependent_environment(self, module, spec, dependent_spec):
- """Called before the install() method of dependents.
+ def environment_modifications(self, dependent_spec):
+ """
+ Called before the install() method of dependents.
+
+ Return the list of environment modifications needed by dependents (or extensions). Default implementation does
+ nothing, but this can be overridden by an extendable package to set up the install environment for its
+ extensions. This is useful if there are some common steps to installing all extensions for a certain package.
- Default implementation does nothing, but this can be
- overridden by an extendable package to set up the install
- environment for its extensions. This is useful if there are
- some common steps to installing all extensions for a
- certain package.
+ Example :
- Some examples:
+ 1. Installing python modules generally requires `PYTHONPATH` to point to the lib/pythonX.Y/site-packages
+ directory in the module's install prefix. This could set that variable.
- 1. Installing python modules generally requires PYTHONPATH to
- point to the lib/pythonX.Y/site-packages directory in the
- module's install prefix. This could set that variable.
+ 2. A lot of Qt extensions need `QTDIR` set. This can be used to do that.
- 2. Extensions often need to invoke the 'python' interpreter
- from the Python installation being extended. This routine can
- put a 'python' Execuable object in the module scope for the
- extension package to simplify extension installs.
+ Args:
+ dependent_spec: dependent (or extension) of this spec
+
+ Returns:
+ instance of environment modifications
+ """
+ return EnvironmentModifications()
+
+ def module_modifications(self, module, spec, dependent_spec):
+ """
+ Called before the install() method of dependents.
+
+ Default implementation does nothing, but this can be overridden by an extendable package to set up the module of
+ its extensions. This is useful if there are some common steps to installing all extensions for a
+ certain package.
- 3. A lot of Qt extensions need QTDIR set. This can be used to do that.
+ Example :
+ 1. Extensions often need to invoke the 'python' interpreter from the Python installation being extended.
+ This routine can put a 'python' Executable object in the module scope for the extension package to simplify
+ extension installs.
"""
pass
diff --git a/lib/spack/spack/test/__init__.py b/lib/spack/spack/test/__init__.py
index d5d8b64765..cd842561e6 100644
--- a/lib/spack/spack/test/__init__.py
+++ b/lib/spack/spack/test/__init__.py
@@ -66,7 +66,8 @@ test_names = ['versions',
'database',
'namespace_trie',
'yaml',
- 'sbang']
+ 'sbang',
+ 'environment']
def list_tests():
diff --git a/lib/spack/spack/test/database.py b/lib/spack/spack/test/database.py
index 9a57e1f03e..ce6e8a0552 100644
--- a/lib/spack/spack/test/database.py
+++ b/lib/spack/spack/test/database.py
@@ -26,6 +26,7 @@
These tests check the database is functioning properly,
both in memory and in its file
"""
+import os.path
import multiprocessing
import shutil
import tempfile
diff --git a/lib/spack/spack/test/environment.py b/lib/spack/spack/test/environment.py
new file mode 100644
index 0000000000..f44282d6f3
--- /dev/null
+++ b/lib/spack/spack/test/environment.py
@@ -0,0 +1,65 @@
+import unittest
+import os
+from spack.environment import EnvironmentModifications, apply_environment_modifications
+
+
+class EnvironmentTest(unittest.TestCase):
+ def setUp(self):
+ os.environ.clear()
+ os.environ['UNSET_ME'] = 'foo'
+ os.environ['EMPTY_PATH_LIST'] = ''
+ os.environ['PATH_LIST'] = '/path/second:/path/third'
+ os.environ['REMOVE_PATH_LIST'] = '/a/b:/duplicate:/a/c:/remove/this:/a/d:/duplicate/:/f/g'
+
+ def test_set_env(self):
+ env = EnvironmentModifications()
+ env.set_env('A', 'dummy value')
+ env.set_env('B', 3)
+ apply_environment_modifications(env)
+ self.assertEqual('dummy value', os.environ['A'])
+ self.assertEqual(str(3), os.environ['B'])
+
+ def test_unset_env(self):
+ env = EnvironmentModifications()
+ self.assertEqual('foo', os.environ['UNSET_ME'])
+ env.unset_env('UNSET_ME')
+ apply_environment_modifications(env)
+ self.assertRaises(KeyError, os.environ.__getitem__, 'UNSET_ME')
+
+ def test_path_manipulation(self):
+ env = EnvironmentModifications()
+
+ env.append_path('PATH_LIST', '/path/last')
+ env.prepend_path('PATH_LIST', '/path/first')
+
+ env.append_path('EMPTY_PATH_LIST', '/path/middle')
+ env.append_path('EMPTY_PATH_LIST', '/path/last')
+ env.prepend_path('EMPTY_PATH_LIST', '/path/first')
+
+ env.append_path('NEWLY_CREATED_PATH_LIST', '/path/middle')
+ env.append_path('NEWLY_CREATED_PATH_LIST', '/path/last')
+ env.prepend_path('NEWLY_CREATED_PATH_LIST', '/path/first')
+
+ env.remove_path('REMOVE_PATH_LIST', '/remove/this')
+ env.remove_path('REMOVE_PATH_LIST', '/duplicate/')
+
+ apply_environment_modifications(env)
+ self.assertEqual('/path/first:/path/second:/path/third:/path/last', os.environ['PATH_LIST'])
+ self.assertEqual('/path/first:/path/middle:/path/last', os.environ['EMPTY_PATH_LIST'])
+ self.assertEqual('/path/first:/path/middle:/path/last', os.environ['NEWLY_CREATED_PATH_LIST'])
+ self.assertEqual('/a/b:/a/c:/a/d:/f/g', os.environ['REMOVE_PATH_LIST'])
+
+ def test_extra_arguments(self):
+ env = EnvironmentModifications()
+ env.set_env('A', 'dummy value', who='Pkg1')
+ apply_environment_modifications(env)
+ self.assertEqual('dummy value', os.environ['A'])
+
+ def test_extend(self):
+ env = EnvironmentModifications()
+ env.set_env('A', 'dummy value')
+ env.set_env('B', 3)
+ copy_construct = EnvironmentModifications(env)
+ self.assertEqual(len(copy_construct), 2)
+ for x, y in zip(env, copy_construct):
+ assert x is y
diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py
index ae8e5708be..00cda8d508 100644
--- a/lib/spack/spack/util/environment.py
+++ b/lib/spack/spack/util/environment.py
@@ -40,11 +40,13 @@ def env_flag(name):
return False
+# FIXME : remove this function ?
def path_set(var_name, directories):
path_str = ":".join(str(dir) for dir in directories)
os.environ[var_name] = path_str
+# FIXME : remove this function ?
def path_put_first(var_name, directories):
"""Puts the provided directories first in the path, adding them
if they're not already there.
@@ -59,12 +61,6 @@ def path_put_first(var_name, directories):
path_set(var_name, new_path)
-def pop_keys(dictionary, *keys):
- for key in keys:
- if key in dictionary:
- dictionary.pop(key)
-
-
def dump_environment(path):
"""Dump the current environment out to a file."""
with open(path, 'w') as env_file:
diff --git a/var/spack/repos/builtin/packages/mpich/package.py b/var/spack/repos/builtin/packages/mpich/package.py
index e2b3654c19..d298981c92 100644
--- a/var/spack/repos/builtin/packages/mpich/package.py
+++ b/var/spack/repos/builtin/packages/mpich/package.py
@@ -46,14 +46,18 @@ class Mpich(Package):
provides('mpi@:3.0', when='@3:')
provides('mpi@:1.3', when='@1:')
- def setup_dependent_environment(self, module, spec, dep_spec):
+ 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'])
+ return env
+
+ def module_modifications(self, module, spec, dep_spec):
"""For dependencies, make mpicc's use spack wrapper."""
- os.environ['MPICH_CC'] = os.environ['CC']
- os.environ['MPICH_CXX'] = os.environ['CXX']
- os.environ['MPICH_F77'] = os.environ['F77']
- os.environ['MPICH_F90'] = os.environ['FC']
- os.environ['MPICH_FC'] = os.environ['FC']
-
+ # FIXME : is this necessary ? Shouldn't this be part of a contract with MPI providers?
module.mpicc = join_path(self.prefix.bin, 'mpicc')
def install(self, spec, prefix):
diff --git a/var/spack/repos/builtin/packages/mvapich2/package.py b/var/spack/repos/builtin/packages/mvapich2/package.py
index af5ed1b088..e4e95f92af 100644
--- a/var/spack/repos/builtin/packages/mvapich2/package.py
+++ b/var/spack/repos/builtin/packages/mvapich2/package.py
@@ -123,7 +123,7 @@ class Mvapich2(Package):
count += 1
if count > 1:
raise RuntimeError('network variants are mutually exclusive (only one can be selected at a time)')
-
+ network_options = []
# From here on I can suppose that only one variant has been selected
if self.enabled(Mvapich2.PSM) in spec:
network_options = ["--with-device=ch3:psm"]
diff --git a/var/spack/repos/builtin/packages/netlib-scalapack/package.py b/var/spack/repos/builtin/packages/netlib-scalapack/package.py
index 22d538560e..ecdea46442 100644
--- a/var/spack/repos/builtin/packages/netlib-scalapack/package.py
+++ b/var/spack/repos/builtin/packages/netlib-scalapack/package.py
@@ -40,11 +40,10 @@ class NetlibScalapack(Package):
make()
make("install")
- def setup_dependent_environment(self, module, spec, dependent_spec):
+ def module_modifications(self, module, spec, dependent_spec):
# TODO treat OS that are not Linux...
lib_suffix = '.so' if '+shared' in spec['scalapack'] else '.a'
spec['scalapack'].fc_link = '-L%s -lscalapack' % spec['scalapack'].prefix.lib
spec['scalapack'].cc_link = spec['scalapack'].fc_link
- spec['scalapack'].libraries = [join_path(spec['scalapack'].prefix.lib,
- 'libscalapack%s' % lib_suffix)]
+ spec['scalapack'].libraries = [join_path(spec['scalapack'].prefix.lib, 'libscalapack%s' % lib_suffix)]
diff --git a/var/spack/repos/builtin/packages/openmpi/package.py b/var/spack/repos/builtin/packages/openmpi/package.py
index e4484af8c5..3a14170457 100644
--- a/var/spack/repos/builtin/packages/openmpi/package.py
+++ b/var/spack/repos/builtin/packages/openmpi/package.py
@@ -41,12 +41,14 @@ class Openmpi(Package):
def url_for_version(self, version):
return "http://www.open-mpi.org/software/ompi/v%s/downloads/openmpi-%s.tar.bz2" % (version.up_to(2), version)
- def setup_dependent_environment(self, module, spec, dep_spec):
- """For dependencies, make mpicc's use spack wrapper."""
- os.environ['OMPI_CC'] = 'cc'
- os.environ['OMPI_CXX'] = 'c++'
- os.environ['OMPI_FC'] = 'f90'
- os.environ['OMPI_F77'] = 'f77'
+ def environment_modifications(self, dependent_spec):
+ env = super(Openmpi, self).environment_modifications(dependent_spec)
+ # FIXME : the compilers should point to the current wrappers, not to generic cc etc.
+ env.set_env('OMPI_CC', 'cc')
+ env.set_env('OMPI_CXX', 'c++')
+ env.set_env('OMPI_FC', 'f90')
+ env.set_env('OMPI_F77', 'f77')
+ return env
def install(self, spec, prefix):
config_args = ["--prefix=%s" % prefix,
diff --git a/var/spack/repos/builtin/packages/python/package.py b/var/spack/repos/builtin/packages/python/package.py
index dd240d1ea0..307cec726b 100644
--- a/var/spack/repos/builtin/packages/python/package.py
+++ b/var/spack/repos/builtin/packages/python/package.py
@@ -89,8 +89,17 @@ class Python(Package):
def site_packages_dir(self):
return os.path.join(self.python_lib_dir, 'site-packages')
+ def environment_modifications(self, extension_spec):
+ env = super(Python, self).environment_modifications(extension_spec)
+ # Set PYTHONPATH to include site-packages dir for the
+ # extension and any other python extensions it depends on.
+ python_paths = []
+ for d in extension_spec.traverse():
+ if d.package.extends(self.spec):
+ python_paths.append(os.path.join(d.prefix, self.site_packages_dir))
+ env.set_env['PYTHONPATH'] = ':'.join(python_paths)
- def setup_dependent_environment(self, module, spec, ext_spec):
+ def module_modifications(self, module, spec, ext_spec):
"""Called before python modules' install() methods.
In most cases, extensions will only need to have one line::
@@ -111,15 +120,6 @@ class Python(Package):
# Make the site packages directory if it does not exist already.
mkdirp(module.site_packages_dir)
- # Set PYTHONPATH to include site-packages dir for the
- # extension and any other python extensions it depends on.
- python_paths = []
- for d in ext_spec.traverse():
- if d.package.extends(self.spec):
- python_paths.append(os.path.join(d.prefix, self.site_packages_dir))
- os.environ['PYTHONPATH'] = ':'.join(python_paths)
-
-
# ========================================================================
# Handle specifics of activating and deactivating python modules.
# ========================================================================
diff --git a/var/spack/repos/builtin/packages/qt/package.py b/var/spack/repos/builtin/packages/qt/package.py
index ef5f05601f..0adf352be2 100644
--- a/var/spack/repos/builtin/packages/qt/package.py
+++ b/var/spack/repos/builtin/packages/qt/package.py
@@ -55,11 +55,10 @@ class Qt(Package):
depends_on("mesa", when='@4:+mesa')
depends_on("libxcb")
-
- def setup_dependent_environment(self, module, spec, dep_spec):
- """Dependencies of Qt find it using the QTDIR environment variable."""
- os.environ['QTDIR'] = self.prefix
-
+ def environment_modifications(self, dependent_spec):
+ env = super(Qt, self).environment_modifications(dependent_spec)
+ env.set_env['QTDIR'] = self.prefix
+ return env
def patch(self):
if self.spec.satisfies('@4'):
diff --git a/var/spack/repos/builtin/packages/ruby/package.py b/var/spack/repos/builtin/packages/ruby/package.py
index 6b6242362c..9caea30ef4 100644
--- a/var/spack/repos/builtin/packages/ruby/package.py
+++ b/var/spack/repos/builtin/packages/ruby/package.py
@@ -1,6 +1,5 @@
from spack import *
-import spack
-import os
+
class Ruby(Package):
"""A dynamic, open source programming language with a focus on
@@ -15,11 +14,22 @@ class Ruby(Package):
def install(self, spec, prefix):
configure("--prefix=%s" % prefix)
-
make()
make("install")
- def setup_dependent_environment(self, module, spec, ext_spec):
+ def environment_modifications(self, extension_spec):
+ env = super(Ruby, self).environment_modifications(extension_spec)
+ # Set GEM_PATH to include dependent gem directories
+ ruby_paths = []
+ for d in extension_spec.traverse():
+ if d.package.extends(self.spec):
+ ruby_paths.append(d.prefix)
+ env.set_env('GEM_PATH', concatenate_paths(ruby_paths))
+ # The actual installation path for this gem
+ env.set_env('GEM_HOME', extension_spec.prefix)
+ return env
+
+ def module_modifications(self, module, spec, ext_spec):
"""Called before ruby modules' install() methods. Sets GEM_HOME
and GEM_PATH to values appropriate for the package being built.
@@ -31,11 +41,4 @@ class Ruby(Package):
module.ruby = Executable(join_path(spec.prefix.bin, 'ruby'))
module.gem = Executable(join_path(spec.prefix.bin, 'gem'))
- # Set GEM_PATH to include dependent gem directories
- ruby_paths = []
- for d in ext_spec.traverse():
- if d.package.extends(self.spec):
- ruby_paths.append(d.prefix)
- os.environ['GEM_PATH'] = ':'.join(ruby_paths)
- # The actual installation path for this gem
- os.environ['GEM_HOME'] = ext_spec.prefix
+