summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/spack/environment.py63
1 files changed, 52 insertions, 11 deletions
diff --git a/lib/spack/spack/environment.py b/lib/spack/spack/environment.py
index d85c67d7fc..1e8665fe01 100644
--- a/lib/spack/spack/environment.py
+++ b/lib/spack/spack/environment.py
@@ -26,9 +26,11 @@ import collections
import inspect
import json
import os
+import re
import sys
import os.path
import subprocess
+from llnl.util.lang import dedupe
class NameModifier(object):
@@ -289,6 +291,12 @@ class EnvironmentModifications(object):
(default: ``&> /dev/null``)
concatenate_on_success (str): Operator used to execute a command
only when the previous command succeeds (default: ``&&``)
+ blacklist ([str or re]): Ignore any modifications of these
+ variables (default: [])
+ whitelist ([str or re]): Always respect modifications of these
+ variables (default: []). Has precedence over blacklist.
+ clean (bool): In addition to removing empty entries,
+ also remove duplicate entries (default: False).
Returns:
EnvironmentModifications: an object that, if executed, has
@@ -305,6 +313,9 @@ class EnvironmentModifications(object):
source_command = kwargs.get('source_command', 'source')
suppress_output = kwargs.get('suppress_output', '&> /dev/null')
concatenate_on_success = kwargs.get('concatenate_on_success', '&&')
+ blacklist = kwargs.get('blacklist', [])
+ whitelist = kwargs.get('whitelist', [])
+ clean = kwargs.get('clean', False)
source_file = [source_command, filename]
source_file.extend(args)
@@ -346,25 +357,46 @@ class EnvironmentModifications(object):
env_after = dict((k.encode('utf-8'), v.encode('utf-8'))
for k, v in env_after.items())
- # Filter variables that are not related to sourcing a file
- to_be_filtered = 'SHLVL', '_', 'PWD', 'OLDPWD', 'PS2'
+ # Other variables unrelated to sourcing a file
+ blacklist.extend(['SHLVL', '_', 'PWD', 'OLDPWD', 'PS2'])
+
+ def set_intersection(fullset, *args):
+ # A set intersection using string literals and regexs
+ meta = '[' + re.escape('[$()*?[]^{|}') + ']'
+ subset = fullset & set(args) # As literal
+ for name in args:
+ if re.search(meta, name):
+ pattern = re.compile(name)
+ for k in fullset:
+ if re.match(pattern, k):
+ subset.add(k)
+ return subset
+
for d in env_after, env_before:
- for name in to_be_filtered:
- d.pop(name, None)
+ # Retain (whitelist) has priority over prune (blacklist)
+ prune = set_intersection(set(d), *blacklist)
+ prune -= set_intersection(prune, *whitelist)
+ for k in prune:
+ d.pop(k, None)
# Fill the EnvironmentModifications instance
env = EnvironmentModifications()
# New variables
- new_variables = set(env_after) - set(env_before)
+ new_variables = list(set(env_after) - set(env_before))
# Variables that have been unset
- unset_variables = set(env_before) - set(env_after)
+ unset_variables = list(set(env_before) - set(env_after))
# Variables that have been modified
- common_variables = set(
- env_before).intersection(set(env_after))
+ common_variables = set(env_before).intersection(set(env_after))
+
modified_variables = [x for x in common_variables
if env_before[x] != env_after[x]]
+ # Consistent output order - looks nicer, easier comparison...
+ new_variables.sort()
+ unset_variables.sort()
+ modified_variables.sort()
+
def return_separator_if_any(*args):
separators = ':', ';'
for separator in separators:
@@ -401,6 +433,14 @@ class EnvironmentModifications(object):
before_list = list(filter(None, before_list))
after_list = list(filter(None, after_list))
+ # Remove duplicate entries (worse matching, bloats env)
+ if clean:
+ before_list = list(dedupe(before_list))
+ after_list = list(dedupe(after_list))
+ # The reassembled cleaned entries
+ before = sep.join(before_list)
+ after = sep.join(after_list)
+
# Paths that have been removed
remove_list = [
ii for ii in before_list if ii not in after_list]
@@ -413,14 +453,15 @@ class EnvironmentModifications(object):
end = after_list.index(remaining_list[-1])
search = sep.join(after_list[start:end + 1])
except IndexError:
- env.prepend_path(x, env_after[x])
+ env.prepend_path(x, after)
if search not in before:
# We just need to set the variable to the new value
- env.prepend_path(x, env_after[x])
+ env.prepend_path(x, after)
else:
try:
prepend_list = after_list[:start]
+ prepend_list.reverse() # Preserve order after prepend
except KeyError:
prepend_list = []
try:
@@ -436,7 +477,7 @@ class EnvironmentModifications(object):
env.prepend_path(x, item)
else:
# We just need to set the variable to the new value
- env.set(x, env_after[x])
+ env.set(x, after)
return env