Repository: spack/spack

Issue:
[proposal]: Add custom paths/settings to environment modules
I think you can already add custom paths eg. export `PYTHONHOME` to a generated environment module using [this](http://software.llnl.gov/spack/basic_usage.html#override-package-api) method in `package.py` but it would be nice to be able to not have to modify the `package.py` file or override it with another local package.

I am thinking something like this for the `modules.yaml` config:

``` yaml
modules:
  ...
  jdk:
    set_env:
      - JAVA_HOME: $prefix
  foo:
    prepend_path:
      - BAR: $prefix.lib
    append_path:
      - BAZ: $prefix.include
```

Where you can reference any of the prefix attribute variables listed [here](http://software.llnl.gov/spack/packaging_guide.html#id12).



Discussion:
I like it!

Can I suggest : 

``` yaml
modules:
  ...
  jdk:
    set_env:
      - JAVA_HOME: {prefix}
  foo:
    prepend_path:
      - BAR: {prefix.lib}
    append_path:
      - BAZ: {prefix.include}
```

in that way you may use python string formatting capabilities out of the box :smile: 

@mwilliammyers I have received a bunch of user feedback for our modules and I'll need to generate something like what you propose, with the only difference that I'll need to permit some token expansion also on the keys, e.g. : 

``` yaml
modules:
    prefix_inspections:
      '':
      - '{name}_ROOT'
    tcl:
      all:
        environment:
          set:
            '{name}_FOO': '{ver}'
```

Did you start already working on this? In case, would it make sense to you to work together in #1135 ? @tgamblin ?

@alalazo sorry this one/your most recent comment slipped by me. I am currently revamping the opencv package because I need to get that installed. I am almost done with that and so I would be happy to work on this with you, if it is not too late.


Resolving patch:
diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py
--- a/lib/spack/spack/environment.py
+++ b/lib/spack/spack/environment.py
@@ -37,6 +37,10 @@ def __init__(self, name, **kwargs):
         self.args = {'name': name}
         self.args.update(kwargs)
 
+    def update_args(self, **kwargs):
+        self.__dict__.update(kwargs)
+        self.args.update(kwargs)
+
 
 class NameValueModifier(object):
 
@@ -44,7 +48,11 @@ def __init__(self, name, value, **kwargs):
         self.name = name
         self.value = value
         self.separator = kwargs.get('separator', ':')
-        self.args = {'name': name, 'value': value, 'delim': self.separator}
+        self.args = {'name': name, 'value': value, 'separator': self.separator}
+        self.args.update(kwargs)
+
+    def update_args(self, **kwargs):
+        self.__dict__.update(kwargs)
         self.args.update(kwargs)
 
 
diff --git a/lib/spack/spack/modules.py b/lib/spack/spack/modules.py
--- a/lib/spack/spack/modules.py
+++ b/lib/spack/spack/modules.py
@@ -272,13 +272,25 @@ def naming_scheme(self):
 
     @property
     def tokens(self):
+        """Tokens that can be substituted in environment variable values
+        and naming schemes
+        """
         tokens = {
             'name': self.spec.name,
             'version': self.spec.version,
-            'compiler': self.spec.compiler
+            'compiler': self.spec.compiler,
+            'prefix': self.spec.package.prefix
         }
         return tokens
 
+    @property
+    def upper_tokens(self):
+        """Tokens that can be substituted in environment variable names"""
+        upper_tokens = {
+            'name': self.spec.name.replace('-', '_').upper()
+        }
+        return upper_tokens
+
     @property
     def use_name(self):
         """
@@ -438,6 +450,11 @@ def prerequisite(self, spec):
 
     def process_environment_command(self, env):
         for command in env:
+            # Token expansion from configuration file
+            name = command.args.get('name', '').format(**self.upper_tokens)
+            value = str(command.args.get('value', '')).format(**self.tokens)
+            command.update_args(name=name, value=value)
+            # Format the line int the module file
             try:
                 yield self.environment_modifications_formats[type(
                     command)].format(**command.args)
@@ -511,9 +528,9 @@ class TclModule(EnvModule):
     name = 'tcl'
     path = join_path(spack.share_path, "modules")
     environment_modifications_formats = {
-        PrependPath: 'prepend-path --delim "{delim}" {name} \"{value}\"\n',
-        AppendPath: 'append-path   --delim "{delim}" {name} \"{value}\"\n',
-        RemovePath: 'remove-path   --delim "{delim}" {name} \"{value}\"\n',
+        PrependPath: 'prepend-path --delim "{separator}" {name} \"{value}\"\n',
+        AppendPath: 'append-path   --delim "{separator}" {name} \"{value}\"\n',
+        RemovePath: 'remove-path   --delim "{separator}" {name} \"{value}\"\n',
         SetEnv: 'setenv {name} \"{value}\"\n',
         UnsetEnv: 'unsetenv {name}\n'
     }


Test patch:
diff --git a/lib/spack/spack/test/modules.py b/lib/spack/spack/test/modules.py
--- a/lib/spack/spack/test/modules.py
+++ b/lib/spack/spack/test/modules.py
@@ -89,7 +89,10 @@ def mock_open(filename, mode):
     'enable': ['tcl'],
     'tcl': {
         'all': {
-            'filter': {'environment_blacklist': ['CMAKE_PREFIX_PATH']}
+            'filter': {'environment_blacklist': ['CMAKE_PREFIX_PATH']},
+            'environment': {
+                'set': {'{name}_ROOT': '{prefix}'}
+            }
         },
         'platform=test target=x86_64': {
             'environment': {
@@ -248,6 +251,8 @@ def test_alter_environment(self):
         self.assertEqual(
             len([x for x in content if 'setenv FOO "foo"' in x]), 1)
         self.assertEqual(len([x for x in content if 'unsetenv BAR' in x]), 1)
+        self.assertEqual(
+            len([x for x in content if 'setenv MPILEAKS_ROOT' in x]), 1)
 
         spec = spack.spec.Spec('libdwarf %clang platform=test target=x86_32')
         content = self.get_modulefile_content(spec)
@@ -262,6 +267,8 @@ def test_alter_environment(self):
             len([x for x in content if 'is-loaded foo/bar' in x]), 1)
         self.assertEqual(
             len([x for x in content if 'module load foo/bar' in x]), 1)
+        self.assertEqual(
+            len([x for x in content if 'setenv LIBDWARF_ROOT' in x]), 1)
 
     def test_blacklist(self):
         spack.modules.CONFIGURATION = configuration_blacklist