Compare commits

...

4 Commits

181 changed files with 1774 additions and 1678 deletions

View File

@@ -0,0 +1,982 @@
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Utilities for setting and modifying environment variables."""
import collections
import contextlib
import inspect
import json
import os
import os.path
import re
import shlex
import sys
import llnl.util.executable as executable
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.path import path_to_os_path
is_windows = sys.platform == "win32"
system_paths = (
["/", "/usr", "/usr/local"]
if not is_windows
else ["C:\\", "C:\\Program Files", "C:\\Program Files (x86)", "C:\\Users", "C:\\ProgramData"]
)
suffixes = ["bin", "bin64", "include", "lib", "lib64"] if not is_windows else []
system_dirs = [os.path.join(p, s) for s in suffixes for p in system_paths] + system_paths
def is_system_path(path):
"""Predicate that given a path returns True if it is a system path,
False otherwise.
Args:
path (str): path to a directory
Returns:
True or False
"""
return path and os.path.normpath(path) in system_dirs
def filter_system_paths(paths):
"""Return only paths that are not system paths."""
return [p for p in paths if not is_system_path(p)]
def deprioritize_system_paths(paths):
"""Put system paths at the end of paths, otherwise preserving order."""
filtered_paths = filter_system_paths(paths)
fp = set(filtered_paths)
return filtered_paths + [p for p in paths if p not in fp]
_shell_set_strings = {
"sh": "export {0}={1};\n",
"csh": "setenv {0} {1};\n",
"fish": "set -gx {0} {1};\n",
"bat": 'set "{0}={1}"\n',
}
_shell_unset_strings = {
"sh": "unset {0};\n",
"csh": "unsetenv {0};\n",
"fish": "set -e {0};\n",
"bat": 'set "{0}="\n',
}
tracing_enabled = False
def prune_duplicate_paths(paths):
"""Returns the paths with duplicates removed, order preserved."""
return list(llnl.util.lang.dedupe(paths))
def get_path(name):
path = os.environ.get(name, "").strip()
if path:
return path.split(os.pathsep)
else:
return []
def env_flag(name):
if name in os.environ:
value = os.environ[name].lower()
return value == "true" or value == "1"
return False
def path_set(var_name, directories):
path_str = os.pathsep.join(str(dir) for dir in directories)
os.environ[var_name] = path_str
def path_put_first(var_name, directories):
"""Puts the provided directories first in the path, adding them
if they're not already there.
"""
path = os.environ.get(var_name, "").split(os.pathsep)
for dir in directories:
if dir in path:
path.remove(dir)
new_path = tuple(directories) + tuple(path)
path_set(var_name, new_path)
@contextlib.contextmanager
def set_env(**kwargs):
"""Temporarily sets and restores environment variables.
Variables can be set as keyword arguments to this function.
"""
saved = {}
for var, value in kwargs.items():
if var in os.environ:
saved[var] = os.environ[var]
if value is None:
if var in os.environ:
del os.environ[var]
else:
os.environ[var] = value
yield
for var, value in kwargs.items():
if var in saved:
os.environ[var] = saved[var]
else:
if var in os.environ:
del os.environ[var]
class NameModifier(object):
def __init__(self, name, **kwargs):
self.name = name
self.separator = kwargs.get("separator", os.pathsep)
self.args = {"name": name, "separator": self.separator}
self.args.update(kwargs)
def __eq__(self, other):
if not isinstance(other, NameModifier):
return False
return self.name == other.name
def update_args(self, **kwargs):
self.__dict__.update(kwargs)
self.args.update(kwargs)
class NameValueModifier(object):
def __init__(self, name, value, **kwargs):
self.name = name
self.value = value
self.separator = kwargs.get("separator", os.pathsep)
self.args = {"name": name, "value": value, "separator": self.separator}
self.args.update(kwargs)
def __eq__(self, other):
if not isinstance(other, NameValueModifier):
return False
return (
self.name == other.name
and self.value == other.value
and self.separator == other.separator
)
def update_args(self, **kwargs):
self.__dict__.update(kwargs)
self.args.update(kwargs)
class SetEnv(NameValueModifier):
def execute(self, env):
tty.debug("SetEnv: {0}={1}".format(self.name, str(self.value)), level=3)
env[self.name] = str(self.value)
class AppendFlagsEnv(NameValueModifier):
def execute(self, env):
tty.debug("AppendFlagsEnv: {0}={1}".format(self.name, str(self.value)), level=3)
if self.name in env and env[self.name]:
env[self.name] += self.separator + str(self.value)
else:
env[self.name] = str(self.value)
class UnsetEnv(NameModifier):
def execute(self, env):
tty.debug("UnsetEnv: {0}".format(self.name), level=3)
# Avoid throwing if the variable was not set
env.pop(self.name, None)
class RemoveFlagsEnv(NameValueModifier):
def execute(self, env):
tty.debug("RemoveFlagsEnv: {0}-{1}".format(self.name, str(self.value)), level=3)
environment_value = env.get(self.name, "")
flags = environment_value.split(self.separator) if environment_value else []
flags = [f for f in flags if f != self.value]
env[self.name] = self.separator.join(flags)
class SetPath(NameValueModifier):
def execute(self, env):
string_path = concatenate_paths(self.value, separator=self.separator)
tty.debug("SetPath: {0}={1}".format(self.name, string_path), level=3)
env[self.name] = string_path
class AppendPath(NameValueModifier):
def execute(self, env):
tty.debug("AppendPath: {0}+{1}".format(self.name, str(self.value)), level=3)
environment_value = env.get(self.name, "")
directories = environment_value.split(self.separator) if environment_value else []
directories.append(path_to_os_path(os.path.normpath(self.value)).pop())
env[self.name] = self.separator.join(directories)
class PrependPath(NameValueModifier):
def execute(self, env):
tty.debug("PrependPath: {0}+{1}".format(self.name, str(self.value)), level=3)
environment_value = env.get(self.name, "")
directories = environment_value.split(self.separator) if environment_value else []
directories = [path_to_os_path(os.path.normpath(self.value)).pop()] + directories
env[self.name] = self.separator.join(directories)
class RemovePath(NameValueModifier):
def execute(self, env):
tty.debug("RemovePath: {0}-{1}".format(self.name, str(self.value)), level=3)
environment_value = env.get(self.name, "")
directories = environment_value.split(self.separator) if environment_value else []
directories = [
path_to_os_path(os.path.normpath(x)).pop()
for x in directories
if x != path_to_os_path(os.path.normpath(self.value)).pop()
]
env[self.name] = self.separator.join(directories)
class DeprioritizeSystemPaths(NameModifier):
def execute(self, env):
tty.debug("DeprioritizeSystemPaths: {0}".format(self.name), level=3)
environment_value = env.get(self.name, "")
directories = environment_value.split(self.separator) if environment_value else []
directories = deprioritize_system_paths(
[path_to_os_path(os.path.normpath(x)).pop() for x in directories]
)
env[self.name] = self.separator.join(directories)
class PruneDuplicatePaths(NameModifier):
def execute(self, env):
tty.debug("PruneDuplicatePaths: {0}".format(self.name), level=3)
environment_value = env.get(self.name, "")
directories = environment_value.split(self.separator) if environment_value else []
directories = prune_duplicate_paths(
[path_to_os_path(os.path.normpath(x)).pop() for x in directories]
)
env[self.name] = self.separator.join(directories)
class EnvironmentModifications(object):
"""Keeps track of requests to modify the current environment.
Each call to a method to modify the environment stores the extra
information on the caller in the request:
* 'filename' : filename of the module where the caller is defined
* 'lineno': line number where the request occurred
* 'context' : line of code that issued the request that failed
"""
def __init__(self, other=None, traced=None):
"""Initializes a new instance, copying commands from 'other'
if it is not None.
Args:
other (EnvironmentModifications): list of environment modifications
to be extended (optional)
traced (bool): enable or disable stack trace inspection to log the origin
of the environment modifications.
"""
self.traced = tracing_enabled if traced is None else bool(traced)
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 _maybe_trace(self, kwargs):
"""Provide the modification with stack trace info so that we can track its
origin to find issues in packages. This is very slow and expensive."""
if not self.traced:
return
stack = inspect.stack()
try:
_, filename, lineno, _, context, index = stack[2]
context = context[index].strip()
except Exception:
filename = "unknown file"
lineno = "unknown line"
context = "unknown context"
kwargs.update({"filename": filename, "lineno": lineno, "context": context})
def set(self, name, value, **kwargs):
"""Stores a request to set an environment variable.
Args:
name: name of the environment variable to be set
value: value of the environment variable
"""
self._maybe_trace(kwargs)
item = SetEnv(name, value, **kwargs)
self.env_modifications.append(item)
def append_flags(self, name, value, sep=" ", **kwargs):
"""
Stores in the current object a request to append to an env variable
Args:
name: name of the environment variable to be appended to
value: value to append to the environment variable
Appends with spaces separating different additions to the variable
"""
self._maybe_trace(kwargs)
kwargs.update({"separator": sep})
item = AppendFlagsEnv(name, value, **kwargs)
self.env_modifications.append(item)
def unset(self, name, **kwargs):
"""Stores a request to unset an environment variable.
Args:
name: name of the environment variable to be unset
"""
self._maybe_trace(kwargs)
item = UnsetEnv(name, **kwargs)
self.env_modifications.append(item)
def remove_flags(self, name, value, sep=" ", **kwargs):
"""
Stores in the current object a request to remove flags from an
env variable
Args:
name: name of the environment variable to be removed from
value: value to remove to the environment variable
sep: separator to assume for environment variable
"""
self._maybe_trace(kwargs)
kwargs.update({"separator": sep})
item = RemoveFlagsEnv(name, value, **kwargs)
self.env_modifications.append(item)
def set_path(self, name, elements, **kwargs):
"""Stores a request to set a path generated from a list.
Args:
name: name o the environment variable to be set.
elements: elements of the path to set.
"""
self._maybe_trace(kwargs)
item = SetPath(name, elements, **kwargs)
self.env_modifications.append(item)
def append_path(self, name, path, **kwargs):
"""Stores 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
"""
self._maybe_trace(kwargs)
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
"""
self._maybe_trace(kwargs)
item = PrependPath(name, path, **kwargs)
self.env_modifications.append(item)
def remove_path(self, name, path, **kwargs):
"""Stores 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
"""
self._maybe_trace(kwargs)
item = RemovePath(name, path, **kwargs)
self.env_modifications.append(item)
def deprioritize_system_paths(self, name, **kwargs):
"""Stores a request to deprioritize system paths in a path list,
otherwise preserving the order.
Args:
name: name of the path list in the environment.
"""
self._maybe_trace(kwargs)
item = DeprioritizeSystemPaths(name, **kwargs)
self.env_modifications.append(item)
def prune_duplicate_paths(self, name, **kwargs):
"""Stores a request to remove duplicates from a path list, otherwise
preserving the order.
Args:
name: name of the path list in the environment.
"""
self._maybe_trace(kwargs)
item = PruneDuplicatePaths(name, **kwargs)
self.env_modifications.append(item)
def group_by_name(self):
"""Returns a dict of the modifications grouped by variable name.
Returns:
dict mapping the environment variable name to the modifications to
be done on it
"""
modifications = collections.defaultdict(list)
for item in self:
modifications[item.name].append(item)
return modifications
def is_unset(self, var_name):
modifications = self.group_by_name()
var_updates = modifications.get(var_name, None)
if not var_updates:
# We did not explicitly unset it
return False
# The last modification must unset the variable for it to be considered
# unset
return type(var_updates[-1]) == UnsetEnv
def clear(self):
"""
Clears the current list of modifications
"""
self.env_modifications = []
def reversed(self):
"""
Returns the EnvironmentModifications object that will reverse self
Only creates reversals for additions to the environment, as reversing
``unset`` and ``remove_path`` modifications is impossible.
Reversable operations are set(), prepend_path(), append_path(),
set_path(), and append_flags().
"""
rev = EnvironmentModifications()
for envmod in reversed(self.env_modifications):
if type(envmod) == SetEnv:
tty.debug("Reversing `Set` environment operation may lose " "original value")
rev.unset(envmod.name)
elif type(envmod) == AppendPath:
rev.remove_path(envmod.name, envmod.value)
elif type(envmod) == PrependPath:
rev.remove_path(envmod.name, envmod.value)
elif type(envmod) == SetPath:
tty.debug("Reversing `SetPath` environment operation may lose " "original value")
rev.unset(envmod.name)
elif type(envmod) == AppendFlagsEnv:
rev.remove_flags(envmod.name, envmod.value)
else:
# This is an un-reversable operation
tty.warn(
"Skipping reversal of unreversable operation"
"%s %s" % (type(envmod), envmod.name)
)
return rev
def apply_modifications(self, env=None):
"""Applies the modifications and clears the list."""
# Use os.environ if not specified
# Do not copy, we want to modify it in place
if env is None:
env = os.environ
modifications = self.group_by_name()
# Apply modifications one variable at a time
for name, actions in sorted(modifications.items()):
for x in actions:
x.execute(env)
def shell_modifications(self, shell="sh", explicit=False, env=None):
"""Return shell code to apply the modifications and clears the list."""
modifications = self.group_by_name()
if env is None:
env = os.environ
new_env = env.copy()
for name, actions in sorted(modifications.items()):
for x in actions:
x.execute(new_env)
if "MANPATH" in new_env and not new_env.get("MANPATH").endswith(":"):
new_env["MANPATH"] += ":"
cmds = ""
for name in sorted(set(modifications)):
new = new_env.get(name, None)
old = env.get(name, None)
if explicit or new != old:
if new is None:
cmds += _shell_unset_strings[shell].format(name)
else:
if sys.platform != "win32":
cmd = _shell_set_strings[shell].format(name, shlex.quote(new_env[name]))
else:
cmd = _shell_set_strings[shell].format(name, new_env[name])
cmds += cmd
return cmds
@staticmethod
def from_sourcing_file(filename, *arguments, **kwargs):
"""Constructs an instance of a
:py:class:`llnl.util.envmod.EnvironmentModifications` object
that has the same effect as sourcing a file.
Args:
filename (str): the file to be sourced
*arguments (list): arguments to pass on the command line
Keyword Args:
shell (str): the shell to use (default: ``bash``)
shell_options (str): options passed to the shell (default: ``-c``)
source_command (str): the command to run (default: ``source``)
suppress_output (str): redirect used to suppress output of command
(default: ``&> /dev/null``)
concatenate_on_success (str): operator used to execute a command
only when the previous command succeeds (default: ``&&``)
exclude ([str or re]): ignore any modifications of these
variables (default: [])
include ([str or re]): always respect modifications of these
variables (default: []). Supersedes any excluded variables.
clean (bool): in addition to removing empty entries,
also remove duplicate entries (default: False).
"""
tty.debug("EnvironmentModifications.from_sourcing_file: {0}".format(filename))
# Check if the file actually exists
if not os.path.isfile(filename):
msg = "Trying to source non-existing file: {0}".format(filename)
raise RuntimeError(msg)
# Prepare include and exclude lists of environment variable names
exclude = kwargs.get("exclude", [])
include = kwargs.get("include", [])
clean = kwargs.get("clean", False)
# Other variables unrelated to sourcing a file
exclude.extend(
[
# Bash internals
"SHLVL",
"_",
"PWD",
"OLDPWD",
"PS1",
"PS2",
"ENV",
# Environment modules v4
"LOADEDMODULES",
"_LMFILES_",
"BASH_FUNC_module()",
"MODULEPATH",
"MODULES_(.*)",
r"(\w*)_mod(quar|share)",
# Lmod configuration
r"LMOD_(.*)",
"MODULERCFILE",
]
)
# Compute the environments before and after sourcing
before = sanitize(
environment_after_sourcing_files(os.devnull, **kwargs),
exclude=exclude,
include=include,
)
file_and_args = (filename,) + arguments
after = sanitize(
environment_after_sourcing_files(file_and_args, **kwargs),
exclude=exclude,
include=include,
)
# Delegate to the other factory
return EnvironmentModifications.from_environment_diff(before, after, clean)
@staticmethod
def from_environment_diff(before, after, clean=False):
"""Constructs an instance of a
:py:class:`llnl.util.envmod.EnvironmentModifications` object
from the diff of two dictionaries.
Args:
before (dict): environment before the modifications are applied
after (dict): environment after the modifications are applied
clean (bool): in addition to removing empty entries, also remove
duplicate entries
"""
# Fill the EnvironmentModifications instance
env = EnvironmentModifications()
# New variables
new_variables = list(set(after) - set(before))
# Variables that have been unset
unset_variables = list(set(before) - set(after))
# Variables that have been modified
common_variables = set(before).intersection(set(after))
modified_variables = [x for x in common_variables if before[x] != 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:
for arg in args:
if separator in arg:
return separator
return None
# Add variables to env.
# Assume that variables with 'PATH' in the name or that contain
# separators like ':' or ';' are more likely to be paths
for x in new_variables:
sep = return_separator_if_any(after[x])
if sep:
env.prepend_path(x, after[x], separator=sep)
elif "PATH" in x:
env.prepend_path(x, after[x])
else:
# We just need to set the variable to the new value
env.set(x, after[x])
for x in unset_variables:
env.unset(x)
for x in modified_variables:
value_before = before[x]
value_after = after[x]
sep = return_separator_if_any(value_before, value_after)
if sep:
before_list = value_before.split(sep)
after_list = value_after.split(sep)
# Filter out empty strings
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(llnl.util.lang.dedupe(before_list))
after_list = list(llnl.util.lang.dedupe(after_list))
# The reassembled cleaned entries
value_before = sep.join(before_list)
value_after = sep.join(after_list)
# Paths that have been removed
remove_list = [ii for ii in before_list if ii not in after_list]
# Check that nothing has been added in the middle of
# before_list
remaining_list = [ii for ii in before_list if ii in after_list]
try:
start = after_list.index(remaining_list[0])
end = after_list.index(remaining_list[-1])
search = sep.join(after_list[start : end + 1])
except IndexError:
env.prepend_path(x, value_after)
continue
if search not in value_before:
# We just need to set the variable to the new value
env.prepend_path(x, value_after)
else:
try:
prepend_list = after_list[:start]
prepend_list.reverse() # Preserve order after prepend
except KeyError:
prepend_list = []
try:
append_list = after_list[end + 1 :]
except KeyError:
append_list = []
for item in remove_list:
env.remove_path(x, item)
for item in append_list:
env.append_path(x, item)
for item in prepend_list:
env.prepend_path(x, item)
else:
# We just need to set the variable to the new value
env.set(x, value_after)
return env
def concatenate_paths(paths, separator=os.pathsep):
"""Concatenates an iterable of paths into a string of paths separated by
separator, defaulting to colon.
Args:
paths: iterable of paths
separator: the separator to use, default ';' windows, ':' otherwise
Returns:
string
"""
return separator.join(str(item) for item in paths)
def set_or_unset_not_first(variable, changes, errstream):
"""Check if we are going to set or unset something after other
modifications have already been requested.
"""
indexes = [
ii
for ii, item in enumerate(changes)
if ii != 0 and not item.args.get("force", False) and type(item) in [SetEnv, UnsetEnv]
]
if indexes:
good = "\t \t{context} at {filename}:{lineno}"
nogood = "\t--->\t{context} at {filename}:{lineno}"
message = "Suspicious requests to set or unset '{var}' found"
errstream(message.format(var=variable))
for ii, item in enumerate(changes):
print_format = nogood if ii in indexes else good
errstream(print_format.format(**item.args))
def validate(env, errstream):
"""Validates the environment modifications to check for the presence of
suspicious patterns. Prompts a warning for everything that was found.
Current checks:
- set or unset variables after other changes on the same variable
Args:
env: list of environment modifications
"""
if not env.traced:
return
modifications = env.group_by_name()
for variable, list_of_changes in sorted(modifications.items()):
set_or_unset_not_first(variable, list_of_changes, errstream)
def inspect_path(root, inspections, exclude=None):
"""Inspects ``root`` to search for the subdirectories in ``inspections``.
Adds every path found to a list of prepend-path commands and returns it.
Args:
root (str): absolute path where to search for subdirectories
inspections (dict): maps relative paths to a list of environment
variables that will be modified if the path exists. The
modifications are not performed immediately, but stored in a
command object that is returned to client
exclude (typing.Callable): optional callable. If present it must accept an
absolute path and return True if it should be excluded from the
inspection
Examples:
The following lines execute an inspection in ``/usr`` to search for
``/usr/include`` and ``/usr/lib64``. If found we want to prepend
``/usr/include`` to ``CPATH`` and ``/usr/lib64`` to ``MY_LIB64_PATH``.
.. code-block:: python
# Set up the dictionary containing the inspection
inspections = {
'include': ['CPATH'],
'lib64': ['MY_LIB64_PATH']
}
# Get back the list of command needed to modify the environment
env = inspect_path('/usr', inspections)
# Eventually execute the commands
env.apply_modifications()
Returns:
instance of EnvironmentModifications containing the requested
modifications
"""
if exclude is None:
exclude = lambda x: False
env = EnvironmentModifications()
# Inspect the prefix to check for the existence of common directories
for relative_path, variables in inspections.items():
expected = os.path.join(root, relative_path)
if os.path.isdir(expected) and not exclude(expected):
for variable in variables:
env.prepend_path(variable, expected)
return env
@contextlib.contextmanager
def preserve_environment(*variables):
"""Ensures that the value of the environment variables passed as
arguments is the same before entering to the context manager and after
exiting it.
Variables that are unset before entering the context manager will be
explicitly unset on exit.
Args:
variables (list): list of environment variables to be preserved
"""
cache = {}
for var in variables:
# The environment variable to be preserved might not be there.
# In that case store None as a placeholder.
cache[var] = os.environ.get(var, None)
yield
for var in variables:
value = cache[var]
msg = "[PRESERVE_ENVIRONMENT]"
if value is not None:
# Print a debug statement if the value changed
if var not in os.environ:
msg += ' {0} was unset, will be reset to "{1}"'
tty.debug(msg.format(var, value))
elif os.environ[var] != value:
msg += ' {0} was set to "{1}", will be reset to "{2}"'
tty.debug(msg.format(var, os.environ[var], value))
os.environ[var] = value
elif var in os.environ:
msg += ' {0} was set to "{1}", will be unset'
tty.debug(msg.format(var, os.environ[var]))
del os.environ[var]
def environment_after_sourcing_files(*files, **kwargs):
"""Returns a dictionary with the environment that one would have
after sourcing the files passed as argument.
Args:
*files: each item can either be a string containing the path
of the file to be sourced or a sequence, where the first element
is the file to be sourced and the remaining are arguments to be
passed to the command line
Keyword Args:
env (dict): the initial environment (default: current environment)
shell (str): the shell to use (default: ``/bin/bash``)
shell_options (str): options passed to the shell (default: ``-c``)
source_command (str): the command to run (default: ``source``)
suppress_output (str): redirect used to suppress output of command
(default: ``&> /dev/null``)
concatenate_on_success (str): operator used to execute a command
only when the previous command succeeds (default: ``&&``)
"""
# Set the shell executable that will be used to source files
shell_cmd = kwargs.get("shell", "/bin/bash")
shell_options = kwargs.get("shell_options", "-c")
source_command = kwargs.get("source_command", "source")
suppress_output = kwargs.get("suppress_output", "&> /dev/null")
concatenate_on_success = kwargs.get("concatenate_on_success", "&&")
shell = executable.Executable(" ".join([shell_cmd, shell_options]))
def _source_single_file(file_and_args, environment):
source_file = [source_command]
source_file.extend(x for x in file_and_args)
source_file = " ".join(source_file)
# If the environment contains 'python' use it, if not
# go with sys.executable. Below we just need a working
# Python interpreter, not necessarily sys.executable.
python_cmd = executable.which("python3", "python", "python2")
python_cmd = python_cmd.path if python_cmd else sys.executable
dump_cmd = "import os, json; print(json.dumps(dict(os.environ)))"
dump_environment = python_cmd + ' -E -c "{0}"'.format(dump_cmd)
# Try to source the file
source_file_arguments = " ".join(
[
source_file,
suppress_output,
concatenate_on_success,
dump_environment,
]
)
output = shell(source_file_arguments, output=str, env=environment, ignore_quotes=True)
return json.loads(output)
current_environment = kwargs.get("env", dict(os.environ))
for f in files:
# Normalize the input to the helper function
if isinstance(f, str):
f = [f]
current_environment = _source_single_file(f, environment=current_environment)
return current_environment
def sanitize(environment, exclude, include):
"""Returns a copy of the input dictionary where all the keys that
match an excluded pattern and don't match an included pattern are
removed.
Args:
environment (dict): input dictionary
exclude (list): literals or regex patterns to be excluded
include (list): literals or regex patterns to be included
"""
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
# Don't modify input, make a copy instead
environment = dict(environment)
# include supersedes any excluded items
prune = set_intersection(set(environment), *exclude)
prune -= set_intersection(prune, *include)
for k in prune:
environment.pop(k, None)
return environment

View File

@@ -8,11 +8,10 @@
import shlex
import subprocess
import sys
from typing import List, Optional, Union
import llnl.util.tty as tty
import spack.error
from spack.util.path import Path, format_os_path, path_to_os_path, system_path_filter
from llnl.util.path import Path, format_os_path, path_to_os_path, system_path_filter
__all__ = ["Executable", "which", "ProcessError"]
@@ -27,13 +26,20 @@ def __init__(self, name):
# filter back to platform dependent path
self.exe = path_to_os_path(*self.exe)
self.default_env = {}
from spack.util.environment import EnvironmentModifications # no cycle
self.default_envmod = EnvironmentModifications()
self._default_envmod = None
self.returncode = None
if not self.exe:
raise ProcessError("Cannot construct executable for '%s'" % name)
raise ProcessError(f"Cannot construct executable for '{name}'")
@property
def default_envmod(self):
from llnl.util.envmod import EnvironmentModifications
if self._default_envmod is None:
self._default_envmod = EnvironmentModifications()
return self._default_envmod
@system_path_filter
def add_default_arg(self, arg):
@@ -122,6 +128,8 @@ def __call__(self, *args, **kwargs):
By default, the subprocess inherits the parent's file descriptors.
"""
from llnl.util.envmod import EnvironmentModifications
# Environment
env_arg = kwargs.get("env", None)
@@ -130,8 +138,6 @@ def __call__(self, *args, **kwargs):
self.default_envmod.apply_modifications(env)
env.update(self.default_env)
from spack.util.environment import EnvironmentModifications # no cycle
# Apply env argument
if isinstance(env_arg, EnvironmentModifications):
env_arg.apply_modifications(env)
@@ -227,27 +233,26 @@ def streamify(arg, mode):
rc = self.returncode = proc.returncode
if fail_on_error and rc != 0 and (rc not in ignore_errors):
long_msg = cmd_line_string
msg = f"Command {cmd_line_string} exited with status {proc.returncode}."
if result:
# If the output is not captured in the result, it will have
# been stored either in the specified files (e.g. if
# 'output' specifies a file) or written to the parent's
# stdout/stderr (e.g. if 'output' is not specified)
long_msg += "\n" + result
msg += "\n" + result
raise ProcessError("Command exited with status %d:" % proc.returncode, long_msg)
raise ProcessError(msg)
return result
except OSError as e:
raise ProcessError("%s: %s" % (self.exe[0], e.strerror), "Command: " + cmd_line_string)
raise ProcessError(f"{self.exe[0]}: {e.strerror}\n Command: '{cmd_line_string}'")
except subprocess.CalledProcessError as e:
if fail_on_error:
raise ProcessError(
str(e),
"\nExit status %d when invoking command: %s"
% (proc.returncode, cmd_line_string),
f"{str(e)}\n"
f" Exit status {proc.returncode} when invoking command: '{cmd_line_string}'"
)
finally:
@@ -275,16 +280,31 @@ def __str__(self):
@system_path_filter
def which_string(*args, **kwargs):
"""Like ``which()``, but return a string instead of an ``Executable``."""
path = kwargs.get("path", os.environ.get("PATH", ""))
required = kwargs.get("required", False)
def which_string(*args: str, path: Optional[Union[List[str], str]] = None, required: bool = False):
"""Finds an executable in the path like command-line which.
If given multiple executables, returns the first one that is found.
If no executables are found, returns None.
Parameters:
*args: One or more executables to search for
Keyword Arguments:
path: colon-separated (semicolon-separated on windows) string or list of
paths to search. Defaults to ``os.environ["PATH"]``
required: If set to ``True``, raise an error if executable not found
Returns:
Absolute path of the first executable found.
"""
if path is None:
path = os.environ.get("PATH") or ""
if isinstance(path, str):
path = path.split(os.pathsep)
for name in args:
win_candidates = []
win_candidates: List[str] = []
if sys.platform == "win32" and (not name.endswith(".exe") and not name.endswith(".bat")):
win_candidates = [name + ext for ext in [".exe", ".bat"]]
candidate_names = [name] if not win_candidates else win_candidates
@@ -309,19 +329,19 @@ def which_string(*args, **kwargs):
return exe
if required:
raise CommandNotFoundError("spack requires '%s'. Make sure it is in your path." % args[0])
raise CommandNotFoundError(args[0])
return None
def which(*args, **kwargs):
def which(*args: str, path: Optional[Union[List[str], str]] = None, required: bool = False):
"""Finds an executable in the path like command-line which.
If given multiple executables, returns the first one that is found.
If no executables are found, returns None.
Parameters:
*args (str): One or more executables to search for
*args: One or more executables to search for
Keyword Arguments:
path (list or str): The path to search. Defaults to ``PATH``
@@ -330,13 +350,17 @@ def which(*args, **kwargs):
Returns:
Executable: The first executable that is found in the path
"""
exe = which_string(*args, **kwargs)
exe = which_string(*args, path=path, required=required)
return Executable(shlex.quote(exe)) if exe else None
class ProcessError(spack.error.SpackError):
class ProcessError(Exception):
"""ProcessErrors are raised when Executables exit with an error code."""
class CommandNotFoundError(spack.error.SpackError):
class CommandNotFoundError(Exception):
"""Raised when ``which()`` can't find a required executable."""
def __init__(self, command: str):
super().__init__(f"Couldn't find command '{command}'. Make sure it is in your path.")
self.command = command

View File

@@ -19,12 +19,11 @@
from sys import platform as _platform
from llnl.util import tty
from llnl.util.executable import CommandNotFoundError, Executable, which
from llnl.util.lang import dedupe, memoized
from llnl.util.path import path_to_os_path, system_path_filter
from llnl.util.symlink import islink, symlink
from spack.util.executable import CommandNotFoundError, Executable, which
from spack.util.path import path_to_os_path, system_path_filter
is_windows = _platform == "win32"
if not is_windows:

View File

@@ -10,11 +10,10 @@
import time
from datetime import datetime
import llnl.util.string
import llnl.util.tty as tty
from llnl.util.lang import pretty_seconds
import spack.util.string
if sys.platform != "win32":
import fcntl
@@ -165,7 +164,7 @@ def _attempts_str(wait_time, nattempts):
if nattempts <= 1:
return ""
attempts = spack.util.string.plural(nattempts, "attempt")
attempts = llnl.util.string.plural(nattempts, "attempt")
return " after {} and {}".format(pretty_seconds(wait_time), attempts)

159
lib/spack/llnl/util/path.py Normal file
View File

@@ -0,0 +1,159 @@
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Utilities for managing Linux and Windows paths."""
# TODO: look at using pathlib since we now only support Python 3
import os
import re
import sys
from urllib.parse import urlparse
is_windows = sys.platform == "win32"
def is_path_url(path):
if "\\" in path:
return False
url_tuple = urlparse(path)
return bool(url_tuple.scheme) and len(url_tuple.scheme) > 1
def win_exe_ext():
return ".exe"
def path_to_os_path(*pths):
"""
Takes an arbitrary number of positional parameters
converts each arguemnt of type string to use a normalized
filepath separator, and returns a list of all values
"""
ret_pths = []
for pth in pths:
if isinstance(pth, str) and not is_path_url(pth):
pth = convert_to_platform_path(pth)
ret_pths.append(pth)
return ret_pths
def sanitize_file_path(pth):
"""
Formats strings to contain only characters that can
be used to generate legal file paths.
Criteria for legal files based on
https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations
Args:
pth: string containing path to be created
on the host filesystem
Return:
sanitized string that can legally be made into a path
"""
# on unix, splitting path by seperators will remove
# instances of illegal characters on join
pth_cmpnts = pth.split(os.path.sep)
if is_windows:
drive_match = r"[a-zA-Z]:"
is_abs = bool(re.match(drive_match, pth_cmpnts[0]))
drive = pth_cmpnts[0] + os.path.sep if is_abs else ""
pth_cmpnts = pth_cmpnts[1:] if drive else pth_cmpnts
illegal_chars = r'[<>?:"|*\\]'
else:
drive = "/" if not pth_cmpnts[0] else ""
illegal_chars = r"[/]"
pth = []
for cmp in pth_cmpnts:
san_cmp = re.sub(illegal_chars, "", cmp)
pth.append(san_cmp)
return drive + os.path.join(*pth)
def system_path_filter(_func=None, arg_slice=None):
"""
Filters function arguments to account for platform path separators.
Optional slicing range can be specified to select specific arguments
This decorator takes all (or a slice) of a method's positional arguments
and normalizes usage of filepath separators on a per platform basis.
Note: **kwargs, urls, and any type that is not a string are ignored
so in such cases where path normalization is required, that should be
handled by calling path_to_os_path directly as needed.
Parameters:
arg_slice (slice): a slice object specifying the slice of arguments
in the decorated method over which filepath separators are
normalized
"""
from functools import wraps
def holder_func(func):
@wraps(func)
def path_filter_caller(*args, **kwargs):
args = list(args)
if arg_slice:
args[arg_slice] = path_to_os_path(*args[arg_slice])
else:
args = path_to_os_path(*args)
return func(*args, **kwargs)
return path_filter_caller
if _func:
return holder_func(_func)
return holder_func
class Path:
"""
Describes the filepath separator types
in an enum style
with a helper attribute
exposing the path type of
the current platform.
"""
unix = 0
windows = 1
platform_path = windows if is_windows else unix
def format_os_path(path, mode=Path.unix):
"""
Format path to use consistent, platform specific
separators. Absolute paths are converted between
drive letters and a prepended '/' as per platform
requirement.
Parameters:
path (str): the path to be normalized, must be a string
or expose the replace method.
mode (Path): the path filesperator style to normalize the
passed path to. Default is unix style, i.e. '/'
"""
if not path:
return path
if mode == Path.windows:
path = path.replace("/", "\\")
else:
path = path.replace("\\", "/")
return path
def convert_to_posix_path(path):
return format_os_path(path, mode=Path.unix)
def convert_to_windows_path(path):
return format_os_path(path, mode=Path.windows)
def convert_to_platform_path(path):
return format_os_path(path, mode=Path.platform_path)

View File

@@ -24,8 +24,7 @@
import traceback
import llnl.util.tty.log as log
from spack.util.executable import which
from llnl.util.executable import which
termios = None
try:

View File

@@ -5,12 +5,12 @@
import os
from llnl.util.executable import Executable, ProcessError
from llnl.util.lang import memoized
import spack.spec
from spack.compilers.clang import Clang
from spack.spec import CompilerSpec
from spack.util.executable import Executable, ProcessError
class ABI(object):

View File

@@ -28,6 +28,7 @@
import llnl.util.filesystem as fsys
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.executable import which
from llnl.util.filesystem import BaseDirectoryVisitor, mkdirp, visit_directory_tree
import spack.cmd
@@ -42,6 +43,7 @@
import spack.store
import spack.util.file_cache as file_cache
import spack.util.gpg
import spack.util.path
import spack.util.spack_json as sjson
import spack.util.spack_yaml as syaml
import spack.util.url as url_util
@@ -50,7 +52,6 @@
from spack.relocate import utf8_paths_to_single_binary_regex
from spack.spec import Spec
from spack.stage import Stage
from spack.util.executable import which
_build_cache_relative_path = "build_cache"
_build_cache_keys_relative_path = "_pgp"

View File

@@ -12,12 +12,12 @@
import archspec.cpu
import llnl.util.envmod
import llnl.util.executable
import llnl.util.filesystem as fs
from llnl.util import tty
import spack.store
import spack.util.environment
import spack.util.executable
from .config import spec_for_current_python
@@ -186,11 +186,11 @@ def _executables_in_store(executables, query_spec, query_info=None):
if (
os.path.exists(bin_dir)
and os.path.isdir(bin_dir)
and spack.util.executable.which_string(*executables, path=bin_dir)
and llnl.util.executable.which_string(*executables, path=bin_dir)
):
spack.util.environment.path_put_first("PATH", [bin_dir])
llnl.util.envmod.path_put_first("PATH", [bin_dir])
if query_info is not None:
query_info["command"] = spack.util.executable.which(*executables, path=bin_dir)
query_info["command"] = llnl.util.executable.which(*executables, path=bin_dir)
query_info["spec"] = concrete_spec
return True
return False

View File

@@ -31,6 +31,8 @@
import uuid
from typing import Callable, List, Optional
import llnl.util.envmod
import llnl.util.executable
from llnl.util import tty
from llnl.util.lang import GroupedExceptionHandler
@@ -46,8 +48,6 @@
import spack.spec
import spack.store
import spack.user_environment
import spack.util.environment
import spack.util.executable
import spack.util.path
import spack.util.spack_yaml
import spack.util.url
@@ -399,7 +399,7 @@ def ensure_module_importable_or_raise(module: str, abstract_spec: Optional[str]
def ensure_executables_in_path_or_raise(
executables: list,
abstract_spec: str,
cmd_check: Optional[Callable[[spack.util.executable.Executable], bool]] = None,
cmd_check: Optional[Callable[[llnl.util.executable.Executable], bool]] = None,
):
"""Ensure that some executables are in path or raise.
@@ -408,7 +408,7 @@ def ensure_executables_in_path_or_raise(
in order. The function exits on the first one found.
abstract_spec (str): abstract spec that provides the executables
cmd_check (object): callable predicate that takes a
``spack.util.executable.Executable`` command and validate it. Should return
``llnl.util.executable.Executable`` command and validate it. Should return
``True`` if the executable is acceptable, ``False`` otherwise.
Can be used to, e.g., ensure a suitable version of the command before
accepting for bootstrapping.
@@ -420,7 +420,7 @@ def ensure_executables_in_path_or_raise(
Executable object
"""
cmd = spack.util.executable.which(*executables)
cmd = llnl.util.executable.which(*executables)
if cmd:
if not cmd_check or cmd_check(cmd):
return cmd
@@ -439,7 +439,7 @@ def ensure_executables_in_path_or_raise(
current_bootstrapper.last_search["spec"],
current_bootstrapper.last_search["command"],
)
env_mods = spack.util.environment.EnvironmentModifications()
env_mods = llnl.util.envmod.EnvironmentModifications()
for dep in concrete_spec.traverse(
root=True, order="post", deptype=("link", "run")
):
@@ -514,7 +514,7 @@ def verify_patchelf(patchelf):
Arguments:
patchelf (spack.util.executable.Executable): patchelf executable
patchelf (llnl.util.executable.Executable): patchelf executable
"""
out = patchelf("--version", output=str, error=os.devnull, fail_on_error=False).strip()
if patchelf.returncode != 0:

View File

@@ -12,12 +12,12 @@
import archspec.cpu
import llnl.util.executable
from llnl.util import tty
import spack.build_environment
import spack.environment
import spack.tengine
import spack.util.executable
from ._common import _root_spec
from .config import root_path, spec_for_current_python, store_path
@@ -120,7 +120,7 @@ def update_syspath_and_environ(self):
)
def _install_with_depfile(self):
spackcmd = spack.util.executable.which("spack")
spackcmd = llnl.util.executable.which("spack")
spackcmd(
"-e",
str(self.environment_root()),
@@ -129,7 +129,7 @@ def _install_with_depfile(self):
"-o",
str(self.environment_root().joinpath("Makefile")),
)
make = spack.util.executable.which("make")
make = llnl.util.executable.which("make")
kwargs = {}
if not tty.is_debug():
kwargs = {"output": os.devnull, "error": os.devnull}

View File

@@ -5,7 +5,7 @@
"""Query the status of bootstrapping on this machine"""
import platform
import spack.util.executable
import llnl.util.executable
from ._common import _executables_in_store, _python_import, _try_import_from_store
from .config import ensure_bootstrap_configuration
@@ -24,7 +24,7 @@ def _required_system_executable(exes, msg):
"""Search for an executable is the system path only."""
if isinstance(exes, str):
exes = (exes,)
if spack.util.executable.which_string(*exes):
if llnl.util.executable.which_string(*exes):
return True, None
return False, msg
@@ -33,7 +33,7 @@ def _required_executable(exes, query_spec, msg):
"""Search for an executable in the system path or in the bootstrap store."""
if isinstance(exes, str):
exes = (exes,)
if spack.util.executable.which_string(*exes) or _executables_in_store(exes, query_spec):
if llnl.util.executable.which_string(*exes) or _executables_in_store(exes, query_spec):
return True, None
return False, msg

View File

@@ -43,7 +43,19 @@
from typing import List, Tuple
import llnl.util.tty as tty
from llnl.util.envmod import (
EnvironmentModifications,
env_flag,
filter_system_paths,
get_path,
inspect_path,
is_system_path,
system_dirs,
validate,
)
from llnl.util.executable import Executable
from llnl.util.lang import dedupe
from llnl.util.string import plural
from llnl.util.symlink import symlink
from llnl.util.tty.color import cescape, colorize
from llnl.util.tty.log import MultiProcessFd
@@ -63,25 +75,12 @@
import spack.store
import spack.subprocess_context
import spack.user_environment
import spack.util.path
import spack.util.pattern
from spack.error import NoHeadersError, NoLibrariesError
from spack.installer import InstallError
from spack.util.cpus import cpus_available
from spack.util.environment import (
EnvironmentModifications,
env_flag,
filter_system_paths,
get_path,
inspect_path,
is_system_path,
system_dirs,
validate,
)
from spack.util.executable import Executable
from spack.util.log_parse import make_log_context, parse_log_events
from spack.util.module_cmd import load_module, module, path_from_modules
from spack.util.string import plural
#
# This can be set by the user to globally disable parallel builds.
@@ -1304,7 +1303,7 @@ class ChildError(InstallError):
# List of errors considered "build errors", for which we'll show log
# context instead of Python context.
build_errors = [("spack.util.executable", "ProcessError")]
build_errors = [("llnl.util.executable", "ProcessError")]
def __init__(self, msg, module, classname, traceback_string, log_name, log_type, context):
super(ChildError, self).__init__(msg)

View File

@@ -2,11 +2,11 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import llnl.util.executable
import llnl.util.filesystem as fs
import spack.directives
import spack.package_base
import spack.util.executable
from .autotools import AutotoolsBuilder, AutotoolsPackage
@@ -22,7 +22,7 @@ def configure(self, pkg, spec, prefix):
prezip = spec["aspell"].prefix.bin.prezip
destdir = prefix
sh = spack.util.executable.which("sh")
sh = llnl.util.executable.which("sh")
sh(
"./configure",
"--vars",

View File

@@ -11,6 +11,7 @@
import llnl.util.filesystem as fs
import llnl.util.tty as tty
from llnl.util.executable import Executable
import spack.build_environment
import spack.builder
@@ -18,7 +19,6 @@
from spack.directives import build_system, conflicts, depends_on
from spack.multimethod import when
from spack.operating_systems.mac_os import macos_version
from spack.util.executable import Executable
from spack.version import Version
from ._checks import (

View File

@@ -15,7 +15,6 @@
import spack.build_environment
import spack.builder
import spack.package_base
import spack.util.path
from spack.directives import build_system, depends_on, variant
from spack.multimethod import when

View File

@@ -11,6 +11,8 @@
import xml.etree.ElementTree as ElementTree
import llnl.util.tty as tty
from llnl.util.envmod import EnvironmentModifications
from llnl.util.executable import Executable
from llnl.util.filesystem import (
HeaderList,
LibraryList,
@@ -25,8 +27,6 @@
import spack.error
from spack.build_environment import dso_suffix
from spack.package_base import InstallError
from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable
from spack.util.prefix import Prefix
from spack.version import Version, ver

View File

@@ -4,11 +4,11 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import llnl.util.executable
from llnl.util.filesystem import find
import spack.builder
import spack.package_base
import spack.util.executable
from spack.directives import build_system, depends_on, extends
from spack.multimethod import when
@@ -41,11 +41,11 @@ class LuaPackage(spack.package_base.PackageBase):
@property
def lua(self):
return spack.util.executable.Executable(self.spec["lua-lang"].prefix.bin.lua)
return llnl.util.executable.Executable(self.spec["lua-lang"].prefix.bin.lua)
@property
def luarocks(self):
lr = spack.util.executable.Executable(self.spec["lua-lang"].prefix.bin.luarocks)
lr = llnl.util.executable.Executable(self.spec["lua-lang"].prefix.bin.luarocks)
return lr

View File

@@ -3,12 +3,12 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import llnl.util.filesystem as fs
from llnl.util.executable import which
import spack.builder
import spack.package_base
from spack.directives import build_system, depends_on
from spack.multimethod import when
from spack.util.executable import which
from ._checks import BaseBuilder

View File

@@ -8,11 +8,11 @@
import shutil
from os.path import basename, dirname, isdir
from llnl.util.envmod import EnvironmentModifications
from llnl.util.executable import Executable
from llnl.util.filesystem import find_headers, find_libraries, join_path
from spack.directives import conflicts, variant
from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable
from .generic import Package

View File

@@ -5,13 +5,13 @@
import inspect
import os
from llnl.util.executable import Executable
from llnl.util.filesystem import filter_file
import spack.builder
import spack.package_base
from spack.directives import build_system, extends
from spack.package_base import PackageBase
from spack.util.executable import Executable
from ._checks import BaseBuilder, execute_build_time_tests

View File

@@ -8,13 +8,13 @@
import llnl.util.filesystem as fs
import llnl.util.lang as lang
import llnl.util.tty as tty
from llnl.util.envmod import env_flag
from llnl.util.executable import Executable, ProcessError
import spack.builder
from spack.build_environment import SPACK_NO_PARALLEL_MAKE, determine_number_of_jobs
from spack.directives import build_system, extends
from spack.package_base import PackageBase
from spack.util.environment import env_flag
from spack.util.executable import Executable, ProcessError
class RacketPackage(PackageBase):

View File

@@ -518,7 +518,7 @@ def setup_build_environment(self, env):
Spack's store.
Args:
env (spack.util.environment.EnvironmentModifications): environment
env (llnl.util.envmod.EnvironmentModifications): environment
modifications to be applied when the package is built. Package authors
can call methods on it to alter the build environment.
"""
@@ -546,7 +546,7 @@ def setup_dependent_build_environment(self, env, dependent_spec):
variable.
Args:
env (spack.util.environment.EnvironmentModifications): environment
env (llnl.util.envmod.EnvironmentModifications): environment
modifications to be applied when the dependent package is built.
Package authors can call methods on it to alter the build environment.

View File

@@ -16,6 +16,7 @@
import ruamel.yaml as yaml
from ruamel.yaml.error import MarkedYAMLError
import llnl.util.string
import llnl.util.tty as tty
from llnl.util.filesystem import join_path
from llnl.util.lang import attr_setdefault, index_by
@@ -33,7 +34,6 @@
import spack.traverse as traverse
import spack.user_environment as uenv
import spack.util.spack_json as sjson
import spack.util.string
# cmd has a submodule called "list" so preserve the python list module
python_list = list
@@ -523,7 +523,7 @@ def print_how_many_pkgs(specs, pkg_type=""):
category, e.g. if pkg_type is "installed" then the message
would be "3 installed packages"
"""
tty.msg("%s" % spack.util.string.plural(len(specs), pkg_type + " package"))
tty.msg("%s" % llnl.util.string.plural(len(specs), pkg_type + " package"))
def spack_is_git_repo():

View File

@@ -11,6 +11,7 @@
import urllib.parse
import llnl.util.tty as tty
from llnl.util.string import plural
import spack.binary_distribution as bindist
import spack.cmd
@@ -30,7 +31,6 @@
from spack.error import SpecError
from spack.spec import Spec, save_dependency_specfiles
from spack.stage import Stage
from spack.util.string import plural
description = "create, download and install binary packages"
section = "packaging"

View File

@@ -6,11 +6,11 @@
import os
import llnl.util.tty as tty
from llnl.util.executable import ProcessError
from llnl.util.filesystem import mkdirp, working_dir
import spack.paths
import spack.util.git
from spack.util.executable import ProcessError
_SPACK_UPSTREAM = "https://github.com/spack/spack"

View File

@@ -10,6 +10,7 @@
import urllib.parse
import llnl.util.tty as tty
from llnl.util.executable import ProcessError, which
from llnl.util.filesystem import mkdirp
import spack.repo
@@ -23,7 +24,6 @@
parse_version,
)
from spack.util.editor import editor
from spack.util.executable import ProcessError, which
from spack.util.naming import (
mod_to_class,
simplify_name,

View File

@@ -12,6 +12,7 @@
from glob import glob
import llnl.util.tty as tty
from llnl.util.executable import which
from llnl.util.filesystem import working_dir
import spack.config
@@ -19,7 +20,6 @@
import spack.platforms
import spack.util.git
from spack.main import get_version
from spack.util.executable import which
description = "debugging commands for troubleshooting Spack"
section = "developer"

View File

@@ -13,7 +13,6 @@
import spack.cmd.common.arguments as arguments
import spack.environment as ev
import spack.solver.asp as asp
import spack.util.environment
import spack.util.spack_json as sjson
description = "compare two specs"

View File

@@ -11,7 +11,9 @@
import tempfile
import llnl.util.filesystem as fs
import llnl.util.string as string
import llnl.util.tty as tty
from llnl.util.envmod import EnvironmentModifications
from llnl.util.tty.colify import colify
from llnl.util.tty.color import colorize
@@ -28,8 +30,6 @@
import spack.schema.env
import spack.tengine
import spack.traverse as traverse
import spack.util.string as string
from spack.util.environment import EnvironmentModifications
description = "manage virtual environments"
section = "environments"

View File

@@ -18,7 +18,6 @@
import spack.cray_manifest as cray_manifest
import spack.detection
import spack.error
import spack.util.environment
description = "manage external packages in Spack configuration"
section = "config"

View File

@@ -5,13 +5,14 @@
import sys
import llnl.util.envmod
import spack.cmd
import spack.cmd.common.arguments as arguments
import spack.cmd.find
import spack.environment as ev
import spack.store
import spack.user_environment as uenv
import spack.util.environment
description = "add package to the user environment"
section = "user environment"
@@ -110,7 +111,7 @@ def load(parser, args):
dep for spec in specs for dep in spec.traverse(root=include_roots, order="post")
]
env_mod = spack.util.environment.EnvironmentModifications()
env_mod = llnl.util.envmod.EnvironmentModifications()
for spec in specs:
env_mod.extend(uenv.environment_modifications_for_spec(spec))
env_mod.prepend_path(uenv.spack_loaded_hashes_var, spec.dag_hash())

View File

@@ -6,10 +6,11 @@
import posixpath
import sys
import llnl.util.executable
from llnl.util.path import convert_to_posix_path
import spack.paths
import spack.util.executable
from spack.spec import Spec
from spack.util.path import convert_to_posix_path
description = "generate Windows installer"
section = "admin"
@@ -100,7 +101,7 @@ def make_installer(parser, args):
spack_logo = posixpath.join(posix_root, "share/spack/logo/favicon.ico")
try:
spack.util.executable.Executable(cmake_path)(
llnl.util.executable.Executable(cmake_path)(
"-S",
source_dir,
"-B",
@@ -111,30 +112,30 @@ def make_installer(parser, args):
"-DSPACK_LOGO=%s" % spack_logo,
"-DSPACK_GIT_VERBOSITY=%s" % git_verbosity,
)
except spack.util.executable.ProcessError:
except llnl.util.executable.ProcessError:
print("Failed to generate installer")
return spack.util.executable.ProcessError.returncode
return llnl.util.executable.ProcessError.returncode
try:
spack.util.executable.Executable(cpack_path)(
llnl.util.executable.Executable(cpack_path)(
"--config", "%s/CPackConfig.cmake" % output_dir, "-B", "%s/" % output_dir
)
except spack.util.executable.ProcessError:
except llnl.util.executable.ProcessError:
print("Failed to generate installer")
return spack.util.executable.ProcessError.returncode
return llnl.util.executable.ProcessError.returncode
try:
spack.util.executable.Executable(os.environ.get("WIX") + "/bin/candle.exe")(
llnl.util.executable.Executable(os.environ.get("WIX") + "/bin/candle.exe")(
"-ext",
"WixBalExtension",
"%s/bundle.wxs" % output_dir,
"-out",
"%s/bundle.wixobj" % output_dir,
)
except spack.util.executable.ProcessError:
except llnl.util.executable.ProcessError:
print("Failed to generate installer chain")
return spack.util.executable.ProcessError.returncode
return llnl.util.executable.ProcessError.returncode
try:
spack.util.executable.Executable(os.environ.get("WIX") + "/bin/light.exe")(
llnl.util.executable.Executable(os.environ.get("WIX") + "/bin/light.exe")(
"-sw1134",
"-ext",
"WixBalExtension",
@@ -142,9 +143,9 @@ def make_installer(parser, args):
"-out",
"%s/Spack.exe" % output_dir,
)
except spack.util.executable.ProcessError:
except llnl.util.executable.ProcessError:
print("Failed to generate installer chain")
return spack.util.executable.ProcessError.returncode
return llnl.util.executable.ProcessError.returncode
print("Successfully generated Spack.exe in %s" % (output_dir))
else:
print("The make-installer command is currently only supported on Windows.")

View File

@@ -10,6 +10,7 @@
import os
import sys
import llnl.util.executable as exe
import llnl.util.tty as tty
from llnl.util.tty.colify import colify
@@ -17,7 +18,6 @@
import spack.cmd.common.arguments as arguments
import spack.paths
import spack.repo
import spack.util.executable as exe
import spack.util.package_hash as ph
description = "query packages associated with particular git revisions"

View File

@@ -10,11 +10,11 @@
import llnl.util.tty as tty
import llnl.util.tty.color as color
from llnl.util.executable import which
from llnl.util.filesystem import working_dir
import spack.paths
import spack.util.git
from spack.util.executable import which
description = "runs source code style checks on spack"
section = "developer"

View File

@@ -5,6 +5,7 @@
import io
import sys
import llnl.util.string
import llnl.util.tty as tty
import llnl.util.tty.colify as colify
@@ -24,7 +25,7 @@ def report_tags(category, tags):
if isatty:
num = len(tags)
fmt = "{0} package tag".format(category)
buffer.write("{0}:\n".format(spack.util.string.plural(num, fmt)))
buffer.write("{0}:\n".format(llnl.util.string.plural(num, fmt)))
if tags:
colify.colify(tags, output=buffer, tty=isatty, indent=4)

View File

@@ -6,11 +6,12 @@
import os
import sys
import llnl.util.envmod
import spack.cmd
import spack.cmd.common.arguments as arguments
import spack.error
import spack.user_environment as uenv
import spack.util.environment
description = "remove package from the user environment"
section = "user environment"
@@ -82,7 +83,7 @@ def unload(parser, args):
)
return 1
env_mod = spack.util.environment.EnvironmentModifications()
env_mod = llnl.util.envmod.EnvironmentModifications()
for spec in specs:
env_mod.extend(uenv.environment_modifications_for_spec(spec).reversed())
env_mod.remove_path(uenv.spack_loaded_hashes_var, spec.dag_hash())

View File

@@ -13,18 +13,18 @@
import tempfile
from typing import List, Optional, Sequence
import llnl.util.envmod as envmod
import llnl.util.executable
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.filesystem import path_contains_subdirectory, paths_containing_libs
from llnl.util.path import system_path_filter
import spack.compilers
import spack.error
import spack.spec
import spack.util.executable
import spack.util.module_cmd
import spack.version
from spack.util.environment import filter_system_paths
from spack.util.path import system_path_filter
__all__ = ["Compiler"]
@@ -40,7 +40,7 @@ def _get_compiler_version_output(compiler_path, version_arg, ignore_errors=()):
compiler_path (path): path of the compiler to be invoked
version_arg (str): the argument used to extract version information
"""
compiler = spack.util.executable.Executable(compiler_path)
compiler = llnl.util.executable.Executable(compiler_path)
if version_arg:
output = compiler(version_arg, output=str, error=str, ignore_errors=ignore_errors)
else:
@@ -54,7 +54,7 @@ def get_compiler_version_output(compiler_path, *args, **kwargs):
# not just executable name. If we don't do this, and the path changes
# (e.g., during testing), we can get incorrect results.
if not os.path.isabs(compiler_path):
compiler_path = spack.util.executable.which_string(compiler_path, required=True)
compiler_path = llnl.util.executable.which_string(compiler_path, required=True)
return _get_compiler_version_output(compiler_path, *args, **kwargs)
@@ -175,7 +175,7 @@ def _parse_non_system_link_dirs(string: str) -> List[str]:
# system paths. Note that 'filter_system_paths' only checks for an
# exact match, while 'in_system_subdirectory' checks if a path contains
# a system directory as a subdirectory
link_dirs = filter_system_paths(link_dirs)
link_dirs = envmod.filter_system_paths(link_dirs)
return list(p for p in link_dirs if not in_system_subdirectory(p))
@@ -339,7 +339,7 @@ def verify_executables(self):
def accessible_exe(exe):
# compilers may contain executable names (on Cray or user edited)
if not os.path.isabs(exe):
exe = spack.util.executable.which_string(exe)
exe = llnl.util.executable.which_string(exe)
if not exe:
return False
return os.path.isfile(exe) and os.access(exe, os.X_OK)
@@ -371,7 +371,7 @@ def real_version(self):
if real_version == spack.version.Version("unknown"):
return self.version
self._real_version = real_version
except spack.util.executable.ProcessError:
except llnl.util.executable.ProcessError:
self._real_version = self.version
return self._real_version
@@ -422,7 +422,7 @@ def _get_compiler_link_paths(self, paths):
csource.write(
"int main(int argc, char* argv[]) { " "(void)argc; (void)argv; return 0; }\n"
)
compiler_exe = spack.util.executable.Executable(first_compiler)
compiler_exe = llnl.util.executable.Executable(first_compiler)
for flag_type in flags:
for flag in self.flags.get(flag_type, []):
compiler_exe.add_default_arg(flag)
@@ -433,7 +433,7 @@ def _get_compiler_link_paths(self, paths):
compiler_exe(self.verbose_flag, fin, "-o", fout, output=str, error=str)
) # str for py2
return _parse_non_system_link_dirs(output)
except spack.util.executable.ProcessError as pe:
except llnl.util.executable.ProcessError as pe:
tty.debug("ProcessError: Command exited with non-zero status: " + pe.long_message)
return []
finally:
@@ -532,7 +532,7 @@ def get_real_version(self):
Use the runtime environment of the compiler (modules and environment
modifications) to enable the compiler to run properly on any platform.
"""
cc = spack.util.executable.Executable(self.cc)
cc = llnl.util.executable.Executable(self.cc)
with self.compiler_environment():
output = cc(
self.version_argument,
@@ -658,7 +658,7 @@ def compiler_environment(self):
spack.util.module_cmd.load_module(module)
# apply other compiler environment changes
env = spack.util.environment.EnvironmentModifications()
env = llnl.util.envmod.EnvironmentModifications()
env.extend(spack.schema.environment.parse(self.environment))
env.apply_modifications()

View File

@@ -17,6 +17,7 @@
import llnl.util.filesystem as fs
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.envmod import get_path
import spack.compiler
import spack.config
@@ -24,7 +25,6 @@
import spack.paths
import spack.platforms
import spack.spec
from spack.util.environment import get_path
from spack.util.naming import mod_to_class
_path_instance_vars = ["cc", "cxx", "f77", "fc"]
@@ -680,7 +680,7 @@ def _default(fn_args):
return value, None
error = "Couldn't get version for compiler {0}".format(path)
except spack.util.executable.ProcessError as e:
except llnl.util.executable.ProcessError as e:
error = "Couldn't get version for compiler {0}\n".format(path) + str(e)
except Exception as e:
# Catching "Exception" here is fine because it just

View File

@@ -6,13 +6,13 @@
import re
import shutil
import llnl.util.executable
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.symlink import symlink
import spack.compiler
import spack.compilers.clang
import spack.util.executable
import spack.version
@@ -90,13 +90,13 @@ def setup_custom_environment(self, pkg, env):
# Use special XCode versions of compiler wrappers when using XCode
# Overwrites build_environment's setting of SPACK_CC and SPACK_CXX
xcrun = spack.util.executable.Executable("xcrun")
xcrun = llnl.util.executable.Executable("xcrun")
xcode_clang = xcrun("-f", "clang", output=str).strip()
xcode_clangpp = xcrun("-f", "clang++", output=str).strip()
env.set("SPACK_CC", xcode_clang, force=True)
env.set("SPACK_CXX", xcode_clangpp, force=True)
xcode_select = spack.util.executable.Executable("xcode-select")
xcode_select = llnl.util.executable.Executable("xcode-select")
# Get the path of the active developer directory
real_root = xcode_select("--print-path", output=str).strip()

View File

@@ -6,11 +6,11 @@
import os
import re
import llnl.util.executable
from llnl.util.filesystem import ancestor
import spack.compiler
import spack.compilers.apple_clang as apple_clang
import spack.util.executable
from spack.version import ver
@@ -204,7 +204,7 @@ def stdcxx_libs(self):
def prefix(self):
# GCC reports its install prefix when running ``-print-search-dirs``
# on the first line ``install: <prefix>``.
cc = spack.util.executable.Executable(self.cc)
cc = llnl.util.executable.Executable(self.cc)
with self.compiler_environment():
gcc_output = cc("-print-search-dirs", output=str, error=str)

View File

@@ -10,10 +10,11 @@
from distutils.version import StrictVersion
from typing import Dict, List, Set
import llnl.util.executable
import spack.compiler
import spack.operating_systems.windows_os
import spack.platforms
import spack.util.executable
from spack.compiler import Compiler
from spack.error import SpackError
from spack.version import Version
@@ -170,7 +171,7 @@ def fc_version(cls, fc):
sps = spack.operating_systems.windows_os.WindowsOs.compiler_search_paths
except AttributeError:
raise SpackError("Windows compiler search paths not established")
clp = spack.util.executable.which_string("cl", path=sps)
clp = llnl.util.executable.which_string("cl", path=sps)
ver = cls.default_version(clp)
else:
ver = fc_ver

View File

@@ -347,7 +347,7 @@ def find_win32_additional_install_paths():
windows_search_ext.extend(
spack.config.get("config:additional_external_search_paths", default=[])
)
windows_search_ext.extend(spack.util.environment.get_path("PATH"))
windows_search_ext.extend(llnl.util.envmod.get_path("PATH"))
return windows_search_ext

View File

@@ -12,10 +12,10 @@
import sys
import warnings
import llnl.util.envmod
import llnl.util.filesystem
import llnl.util.tty
import spack.util.environment
import spack.util.ld_so_conf
from .common import ( # find_windows_compiler_bundled_packages,
@@ -83,9 +83,9 @@ def libraries_in_ld_and_system_library_path(path_hints=None):
"""
path_hints = (
path_hints
or spack.util.environment.get_path("LD_LIBRARY_PATH")
+ spack.util.environment.get_path("DYLD_LIBRARY_PATH")
+ spack.util.environment.get_path("DYLD_FALLBACK_LIBRARY_PATH")
or llnl.util.envmod.get_path("LD_LIBRARY_PATH")
+ llnl.util.envmod.get_path("DYLD_LIBRARY_PATH")
+ llnl.util.envmod.get_path("DYLD_FALLBACK_LIBRARY_PATH")
+ spack.util.ld_so_conf.host_dynamic_linker_search_paths()
)
search_paths = llnl.util.filesystem.search_paths_for_libraries(*path_hints)
@@ -93,7 +93,7 @@ def libraries_in_ld_and_system_library_path(path_hints=None):
def libraries_in_windows_paths(path_hints):
path_hints.extend(spack.util.environment.get_path("PATH"))
path_hints.extend(llnl.util.envmod.get_path("PATH"))
search_paths = llnl.util.filesystem.search_paths_for_libraries(*path_hints)
# on Windows, some libraries (.dlls) are found in the bin directory or sometimes
# at the search root. Add both of those options to the search scheme
@@ -236,7 +236,7 @@ def by_executable(packages_to_check, path_hints=None):
path_hints (list): list of paths to be searched. If None the list will be
constructed based on the PATH environment variable.
"""
path_hints = spack.util.environment.get_path("PATH") if path_hints is None else path_hints
path_hints = llnl.util.envmod.get_path("PATH") if path_hints is None else path_hints
exe_pattern_to_pkgs = collections.defaultdict(list)
for pkg in packages_to_check:
if hasattr(pkg, "executables"):

View File

@@ -20,6 +20,7 @@
import spack.spec
import spack.util.spack_json as sjson
from spack.error import SpackError
from spack.util.environment import get_host_environment_metadata
is_windows = sys.platform == "win32"
# Note: Posixpath is used here as opposed to
@@ -120,8 +121,6 @@ def write_host_environment(self, spec):
versioning. We use it in the case that an analysis later needs to
easily access this information.
"""
from spack.util.environment import get_host_environment_metadata
env_file = self.env_metadata_path(spec)
environ = get_host_environment_metadata()
with open(env_file, "w") as fd:

View File

@@ -16,6 +16,7 @@
import ruamel.yaml as yaml
import llnl.util.envmod
import llnl.util.filesystem as fs
import llnl.util.tty as tty
from llnl.util.lang import dedupe
@@ -1535,7 +1536,7 @@ def check_views(self):
)
def _env_modifications_for_default_view(self, reverse=False):
all_mods = spack.util.environment.EnvironmentModifications()
all_mods = llnl.util.envmod.EnvironmentModifications()
visited = set()
@@ -1576,7 +1577,7 @@ def add_default_view_to_env(self, env_mod):
default view. Removes duplicate paths.
Args:
env_mod (spack.util.environment.EnvironmentModifications): the environment
env_mod (llnl.util.envmod.EnvironmentModifications): the environment
modifications object that is modified.
"""
if default_view_name not in self.views:
@@ -1603,7 +1604,7 @@ def rm_default_view_from_env(self, env_mod):
default view. Reverses the action of ``add_default_view_to_env``.
Args:
env_mod (spack.util.environment.EnvironmentModifications): the environment
env_mod (llnl.util.envmod.EnvironmentModifications): the environment
modifications object that is modified.
"""
if default_view_name not in self.views:

View File

@@ -5,12 +5,12 @@
import os
import llnl.util.tty as tty
from llnl.util.envmod import EnvironmentModifications
from llnl.util.tty.color import colorize
import spack.environment as ev
import spack.repo
import spack.store
from spack.util.environment import EnvironmentModifications
def activate_header(env, shell, prompt=None):
@@ -111,7 +111,7 @@ def activate(env, use_env_repo=False, add_view=True):
add_view (bool): generate commands to add view to path variables
Returns:
spack.util.environment.EnvironmentModifications: Environment variables
llnl.util.envmod.EnvironmentModifications: Environment variables
modifications to activate environment.
"""
ev.activate(env, use_env_repo=use_env_repo)
@@ -150,7 +150,7 @@ def deactivate():
after activation are not unloaded.
Returns:
spack.util.environment.EnvironmentModifications: Environment variables
llnl.util.envmod.EnvironmentModifications: Environment variables
modifications to activate environment.
"""
env_mods = EnvironmentModifications()

View File

@@ -35,6 +35,7 @@
import llnl.util
import llnl.util.filesystem as fs
import llnl.util.tty as tty
from llnl.util.executable import CommandNotFoundError, which
from llnl.util.filesystem import (
get_single_file,
mkdirp,
@@ -42,6 +43,7 @@
temp_rename,
working_dir,
)
from llnl.util.string import comma_and, quote
from llnl.util.symlink import symlink
import spack.config
@@ -54,8 +56,6 @@
import spack.util.web as web_util
import spack.version
from spack.util.compression import decompressor_for, extension_from_path
from spack.util.executable import CommandNotFoundError, which
from spack.util.string import comma_and, quote
#: List of all fetch strategies, created by FetchStrategy metaclass.
all_strategies = []

View File

@@ -6,6 +6,7 @@
import os
import llnl.util.tty as tty
from llnl.util.executable import Executable
from llnl.util.filesystem import BaseDirectoryVisitor, visit_directory_tree
from llnl.util.lang import elide_list
@@ -13,7 +14,6 @@
import spack.config
import spack.relocate
from spack.util.elf import ElfParsingError, parse_elf
from spack.util.executable import Executable
def is_shared_library_elf(filepath):

View File

@@ -6,11 +6,11 @@
import os
import llnl.util.tty as tty
from llnl.util.executable import Executable, which
from llnl.util.filesystem import mkdirp
from llnl.util.symlink import symlink
from spack.util.editor import editor
from spack.util.executable import Executable, which
def pre_install(spec):

View File

@@ -13,6 +13,7 @@
import spack.error
import spack.paths
import spack.util.path
import spack.util.prefix
import spack.util.spack_json as sjson
from spack.spec import Spec

View File

@@ -40,6 +40,8 @@
import llnl.util.filesystem as fs
import llnl.util.lock as lk
import llnl.util.tty as tty
from llnl.util.envmod import EnvironmentModifications
from llnl.util.executable import which
from llnl.util.lang import pretty_seconds
from llnl.util.tty.color import colorize
from llnl.util.tty.log import log_output
@@ -53,11 +55,9 @@
import spack.package_prefs as prefs
import spack.repo
import spack.store
import spack.util.executable
import spack.util.path
import spack.util.timer as timer
from spack.util.environment import EnvironmentModifications, dump_environment
from spack.util.executable import which
from spack.util.environment import dump_environment
#: Counter to support unique spec sequencing that is used to ensure packages
#: with the same priority are (initially) processed in the order in which they

View File

@@ -26,6 +26,7 @@
import archspec.cpu
import llnl.util.envmod
import llnl.util.lang
import llnl.util.tty as tty
import llnl.util.tty.colify
@@ -44,9 +45,7 @@
import spack.spec
import spack.store
import spack.util.debug
import spack.util.environment
import spack.util.git
import spack.util.path
from spack.error import SpackError
#: names of profile statistics
@@ -575,7 +574,7 @@ def setup_main_options(args):
if args.debug:
spack.util.debug.register_interrupt_handler()
spack.config.set("config:debug", True, scope="command_line")
spack.util.environment.tracing_enabled = True
llnl.util.envmod.tracing_enabled = True
if args.timestamp:
tty.set_timestamp(True)

View File

@@ -36,6 +36,7 @@
import re
from typing import Optional
import llnl.util.envmod
import llnl.util.filesystem
import llnl.util.tty as tty
from llnl.util.lang import dedupe
@@ -51,7 +52,6 @@
import spack.schema.environment
import spack.store
import spack.tengine as tengine
import spack.util.environment
import spack.util.file_permissions as fp
import spack.util.path
import spack.util.spack_yaml as syaml
@@ -732,8 +732,8 @@ def environment_modifications(self):
spec.prefix = view.get_projection_for_spec(spec)
env = spack.util.environment.inspect_path(
spec.prefix, prefix_inspections, exclude=spack.util.environment.is_system_path
env = llnl.util.envmod.inspect_path(
spec.prefix, prefix_inspections, exclude=llnl.util.envmod.is_system_path
)
# Let the extendee/dependency modify their extensions/dependencies

View File

@@ -9,6 +9,7 @@
import posixpath
from typing import Any, Dict
import llnl.util.envmod
import llnl.util.lang as lang
import spack.compilers
@@ -17,7 +18,6 @@
import spack.repo
import spack.spec
import spack.tengine as tengine
import spack.util.environment
from .common import BaseConfiguration, BaseContext, BaseFileLayout, BaseModuleFileWriter
@@ -71,7 +71,7 @@ def guess_core_compilers(name, store=False):
# A compiler is considered to be a core compiler if any of the
# C, C++ or Fortran compilers reside in a system directory
is_system_compiler = any(
os.path.dirname(x) in spack.util.environment.system_dirs
os.path.dirname(x) in llnl.util.envmod.system_dirs
for x in compiler["paths"].values()
if x is not None
)

View File

@@ -10,8 +10,8 @@
import llnl.util.filesystem as fs
import llnl.util.lang
import llnl.util.tty as tty
from llnl.util.envmod import get_path
from spack.util.environment import get_path
from spack.util.module_cmd import module
from .linux_distro import LinuxDistro

View File

@@ -8,8 +8,8 @@
import re
import llnl.util.lang
from llnl.util.executable import Executable
from spack.util.executable import Executable
from spack.version import Version
from ._operating_system import OperatingSystem
@@ -50,7 +50,7 @@ def macos_version():
try:
output = Executable("sw_vers")(output=str, fail_on_error=False)
except Exception:
# FileNotFoundError, or spack.util.executable.ProcessError
# FileNotFoundError, or llnl.util.executable.ProcessError
pass
else:
match = re.search(r"ProductVersion:\s*([0-9.]+)", output)

View File

@@ -19,12 +19,12 @@
# import most common types used in packages
from typing import Dict, List, Optional
import llnl.util.executable
import llnl.util.filesystem
from llnl.util.executable import *
from llnl.util.filesystem import *
from llnl.util.symlink import symlink
import spack.util.executable
# These props will be overridden when the build env is set up.
from spack.build_environment import MakeExecutable
from spack.build_systems.aspell_dict import AspellDictPackage
@@ -87,7 +87,6 @@
on_package_attributes,
)
from spack.spec import InvalidSpecDetected, Spec
from spack.util.executable import *
from spack.variant import (
any_combination_of,
auto_or_any_combination_of,

View File

@@ -29,8 +29,11 @@
import warnings
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Type
import llnl.util.envmod
import llnl.util.filesystem as fsys
import llnl.util.path
import llnl.util.tty as tty
from llnl.util.executable import ProcessError, which
from llnl.util.lang import classproperty, memoized, nullcontext
from llnl.util.link_tree import LinkTree
@@ -51,14 +54,12 @@
import spack.spec
import spack.store
import spack.url
import spack.util.environment
import spack.util.path
import spack.util.web
from spack.filesystem_view import YamlFilesystemView
from spack.install_test import TestFailure, TestSuite
from spack.installer import InstallError, PackageInstaller
from spack.stage import ResourceStage, Stage, StageComposite, stage_prefix
from spack.util.executable import ProcessError, which
from spack.util.package_hash import package_hash
from spack.util.prefix import Prefix
from spack.util.web import FetchError
@@ -204,9 +205,9 @@ def __init__(cls, name, bases, attr_dict):
def platform_executables(cls):
def to_windows_exe(exe):
if exe.endswith("$"):
exe = exe.replace("$", "%s$" % spack.util.path.win_exe_ext())
exe = exe.replace("$", "%s$" % llnl.util.path.win_exe_ext())
else:
exe += spack.util.path.win_exe_ext()
exe += llnl.util.path.win_exe_ext()
return exe
plat_exe = []
@@ -1993,7 +1994,7 @@ def run_test(
for line in out:
print(line.rstrip("\n"))
if exc_type is spack.util.executable.ProcessError:
if exc_type is llnl.util.executable.ProcessError:
out = io.StringIO()
spack.build_environment.write_log_summary(
out, "test", self.test_log_file, last=1
@@ -2101,7 +2102,7 @@ def setup_run_environment(self, env):
"""Sets up the run environment for a package.
Args:
env (spack.util.environment.EnvironmentModifications): environment
env (llnl.util.envmod.EnvironmentModifications): environment
modifications to be applied when the package is run. Package authors
can call methods on it to alter the run environment.
"""
@@ -2118,7 +2119,7 @@ def setup_dependent_run_environment(self, env, dependent_spec):
for dependencies.
Args:
env (spack.util.environment.EnvironmentModifications): environment
env (llnl.util.envmod.EnvironmentModifications): environment
modifications to be applied when the dependent package is run.
Package authors can call methods on it to alter the build environment.

View File

@@ -5,7 +5,7 @@
import os
from spack.util.executable import Executable, which
from llnl.util.executable import Executable, which
def compile_c_and_execute(source_file, include_flags, link_flags):

View File

@@ -11,6 +11,7 @@
import llnl.util.filesystem
import llnl.util.lang
from llnl.util.executable import which, which_string
import spack
import spack.error
@@ -20,19 +21,9 @@
import spack.util.spack_json as sjson
from spack.util.compression import allowed_archive
from spack.util.crypto import Checker, checksum
from spack.util.executable import which, which_string
def apply_patch(stage, patch_path, level=1, working_dir="."):
"""Apply the patch at patch_path to code in the stage.
Args:
stage (spack.stage.Stage): stage with code that will be patched
patch_path (str): filesystem location for the patch to apply
level (int or None): patch level (default 1)
working_dir (str): relative path *within* the stage to change to
(default '.')
"""
def get_patch_exe():
git_utils_path = os.environ.get("PATH", "")
if sys.platform == "win32":
git = which_string("git", required=True)
@@ -46,7 +37,20 @@ def apply_patch(stage, patch_path, level=1, working_dir="."):
# Note for future developers: The GNU port of patch to windows
# has issues handling CRLF line endings unless the --binary
# flag is passed.
patch = which("patch", required=True, path=git_utils_path)
return which("patch", required=True, path=git_utils_path)
def apply_patch(stage, patch_path, level=1, working_dir="."):
"""Apply the patch at patch_path to code in the stage.
Args:
stage (spack.stage.Stage): stage with code that will be patched
patch_path (str): filesystem location for the patch to apply
level (int or None): patch level (default 1)
working_dir (str): relative path *within* the stage to change to
(default '.')
"""
patch = get_patch_exe()
with llnl.util.filesystem.working_dir(stage.source_path):
patch("-s", "-p", str(level), "-i", patch_path, "-d", working_dir)

View File

@@ -4,10 +4,9 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import contextlib
import llnl.util.envmod
import llnl.util.lang
import spack.util.environment
from .cray import Cray
from .darwin import Darwin
from .linux import Linux
@@ -64,7 +63,7 @@ def prevent_cray_detection():
"""Context manager that prevents the detection of the Cray platform"""
reset()
try:
with spack.util.environment.set_env(MODULEPATH=""):
with llnl.util.envmod.set_env(MODULEPATH=""):
yield
finally:
reset()

View File

@@ -10,13 +10,13 @@
import archspec.cpu
import llnl.util.tty as tty
from llnl.util.executable import Executable
import spack.target
import spack.version
from spack.operating_systems.cray_backend import CrayBackend
from spack.operating_systems.cray_frontend import CrayFrontend
from spack.paths import build_env_path
from spack.util.executable import Executable
from spack.util.module_cmd import module
from ._platform import NoPlatformError, Platform

View File

@@ -12,6 +12,7 @@
import macholib.mach_o
import macholib.MachO
import llnl.util.executable as executable
import llnl.util.filesystem as fs
import llnl.util.lang
import llnl.util.tty as tty
@@ -24,7 +25,6 @@
import spack.spec
import spack.store
import spack.util.elf as elf
import spack.util.executable as executable
is_macos = str(spack.platforms.real_host()) == "darwin"

View File

@@ -30,6 +30,7 @@
import llnl.util.filesystem as fs
import llnl.util.lang
import llnl.util.path
import llnl.util.tty as tty
from llnl.util.filesystem import working_dir
@@ -563,7 +564,7 @@ def __init__(self, package_checker, namespace, cache):
self.checker = package_checker
self.packages_path = self.checker.packages_path
if sys.platform == "win32":
self.packages_path = spack.util.path.convert_to_posix_path(self.packages_path)
self.packages_path = llnl.util.path.convert_to_posix_path(self.packages_path)
self.namespace = namespace
self.indexers = {}

View File

@@ -40,9 +40,9 @@ def parse(config_obj):
config_obj: a configuration dictionary conforming to the
schema definition for environment modifications
"""
import spack.util.environment as ev
import llnl.util.envmod as envmod
env = ev.EnvironmentModifications()
env = envmod.EnvironmentModifications()
for command, variable in config_obj.items():
# Distinguish between commands that take only a name as argument
# (e.g. unset) and commands that take a name and a value.

View File

@@ -59,8 +59,11 @@
import ruamel.yaml as yaml
import llnl.util.executable
import llnl.util.filesystem as fs
import llnl.util.lang as lang
import llnl.util.path as pth
import llnl.util.string
import llnl.util.tty as tty
import llnl.util.tty.color as clr
@@ -79,14 +82,11 @@
import spack.target
import spack.traverse as traverse
import spack.util.crypto
import spack.util.executable
import spack.util.hash
import spack.util.module_cmd as md
import spack.util.path as pth
import spack.util.prefix
import spack.util.spack_json as sjson
import spack.util.spack_yaml as syaml
import spack.util.string
import spack.variant as vt
import spack.version as vn
@@ -988,7 +988,7 @@ def _command_default_handler(descriptor, spec, cls):
path = os.path.join(home.bin, spec.name)
if fs.is_exe(path):
return spack.util.executable.Executable(path)
return llnl.util.executable.Executable(path)
else:
msg = "Unable to locate {0} command in {1}"
raise RuntimeError(msg.format(spec.name, home.bin))
@@ -5041,7 +5041,7 @@ class InvalidDependencyError(spack.error.SpecError):
def __init__(self, pkg, deps):
self.invalid_deps = deps
super(InvalidDependencyError, self).__init__(
"Package {0} does not depend on {1}".format(pkg, spack.util.string.comma_or(deps))
"Package {0} does not depend on {1}".format(pkg, llnl.util.string.comma_or(deps))
)

View File

@@ -28,6 +28,7 @@
partition_path,
remove_linked_tree,
)
from llnl.util.path import sanitize_file_path
import spack.caches
import spack.config
@@ -378,7 +379,7 @@ def expected_archive_files(self):
expanded = True
if isinstance(self.default_fetcher, fs.URLFetchStrategy):
expanded = self.default_fetcher.expand_archive
clean_url = os.path.basename(sup.sanitize_file_path(self.default_fetcher.url))
clean_url = os.path.basename(sanitize_file_path(self.default_fetcher.url))
fnames.append(clean_url)
if self.mirror_paths:

View File

@@ -7,6 +7,7 @@
import archspec.cpu
import llnl.util.executable
import llnl.util.tty as tty
import spack.compiler
@@ -161,7 +162,7 @@ def optimization_flags(self, compiler):
compiler = spack.compilers.compilers_for_spec(compiler).pop()
try:
compiler_version = compiler.real_version
except spack.util.executable.ProcessError as e:
except llnl.util.executable.ProcessError as e:
# log this and just return compiler.version instead
tty.debug(str(e))

View File

@@ -10,7 +10,10 @@
import pytest
from llnl.util.envmod import EnvironmentModifications
from llnl.util.executable import Executable
from llnl.util.filesystem import HeaderList, LibraryList
from llnl.util.path import Path, convert_to_platform_path
import spack.build_environment
import spack.config
@@ -23,9 +26,6 @@
dso_suffix,
)
from spack.paths import build_env_path
from spack.util.environment import EnvironmentModifications
from spack.util.executable import Executable
from spack.util.path import Path, convert_to_platform_path
def os_pathsep_join(path, *pths):

View File

@@ -7,9 +7,10 @@
import pytest
import llnl.util.executable
import spack.cmd.create
import spack.stage
import spack.util.executable
import spack.util.url as url_util
pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
@@ -46,7 +47,7 @@ def url_and_build_system(request, tmpdir):
the appropriate file name and returns their url along with
the correct build-system guess
"""
tar = spack.util.executable.which("tar")
tar = llnl.util.executable.which("tar")
orig_dir = tmpdir.chdir()
filename, system = request.param
tmpdir.ensure("archive", filename)

View File

@@ -11,6 +11,7 @@
import pytest
import llnl.util.filesystem as fs
from llnl.util.executable import which
import spack.build_systems.autotools
import spack.build_systems.cmake
@@ -19,7 +20,6 @@
import spack.repo
from spack.build_environment import ChildError, setup_package
from spack.spec import Spec
from spack.util.executable import which
DATA_PATH = os.path.join(spack.paths.test_path, "data")

View File

@@ -12,12 +12,13 @@
import pytest
from llnl.util.envmod import set_env, system_dirs
from llnl.util.executable import Executable, ProcessError
import spack.build_environment
import spack.config
import spack.spec
from spack.paths import build_env_path
from spack.util.environment import set_env, system_dirs
from spack.util.executable import Executable, ProcessError
#
# Complicated compiler test command

View File

@@ -7,13 +7,14 @@
import pytest
from llnl.util.path import convert_to_posix_path
import spack.bootstrap
import spack.bootstrap.core
import spack.config
import spack.environment as ev
import spack.main
import spack.mirror
from spack.util.path import convert_to_posix_path
_bootstrap = spack.main.SpackCommand("bootstrap")

View File

@@ -7,11 +7,12 @@
import pytest
from llnl.util.executable import which
import spack.cmd.create
import spack.util.editor
from spack.main import SpackCommand
from spack.url import UndetectableNameError
from spack.util.executable import which
create = SpackCommand("create")

View File

@@ -10,10 +10,11 @@
import pytest
from llnl.util.executable import which
import spack.config
import spack.platforms
from spack.main import SpackCommand, get_version
from spack.util.executable import which
debug = SpackCommand("debug")

View File

@@ -12,6 +12,7 @@
import spack.environment as ev
import spack.spec
import spack.util.path
from spack.main import SpackCommand
develop = SpackCommand("develop")

View File

@@ -14,6 +14,7 @@
import llnl.util.filesystem as fs
import llnl.util.link_tree
from llnl.util.executable import Executable
import spack.cmd.env
import spack.config
@@ -28,7 +29,6 @@
from spack.main import SpackCommand, SpackCommandError
from spack.spec import Spec
from spack.stage import stage_prefix
from spack.util.executable import Executable
from spack.util.path import substitute_path_variables
from spack.version import Version
@@ -638,7 +638,7 @@ def test_env_view_external_prefix(tmpdir_factory, mutable_database, mock_package
e.install_all()
e.write()
env_mod = spack.util.environment.EnvironmentModifications()
env_mod = llnl.util.envmod.EnvironmentModifications()
e.add_default_view_to_env(env_mod)
env_variables = {}
env_mod.apply_modifications(env_variables)

View File

@@ -8,6 +8,7 @@
import pytest
import llnl.util.path
from llnl.util.filesystem import getuid, touch
import spack
@@ -35,7 +36,7 @@ def _platform_executables(monkeypatch):
def _win_exe_ext():
return ".bat"
monkeypatch.setattr(spack.util.path, "win_exe_ext", _win_exe_ext)
monkeypatch.setattr(llnl.util.path, "win_exe_ext", _win_exe_ext)
def define_plat_exe(exe):

View File

@@ -9,13 +9,12 @@
import pytest
import llnl.util.filesystem as fs
from llnl.util.executable import ProcessError
import spack.bootstrap
import spack.util.executable
import spack.util.gpg
from spack.main import SpackCommand
from spack.paths import mock_gpg_data_path, mock_gpg_keys_path
from spack.util.executable import ProcessError
#: spack command used by tests below
gpg = SpackCommand("gpg")

View File

@@ -23,7 +23,6 @@
import spack.environment as ev
import spack.hash_types as ht
import spack.package_base
import spack.util.executable
from spack.error import SpackError
from spack.main import SpackCommand
from spack.parser import SpecSyntaxError

View File

@@ -9,13 +9,13 @@
import pytest
from llnl.util.executable import which
from llnl.util.filesystem import FileFilter
import spack.main
import spack.paths
import spack.repo
from spack.cmd.style import changed_files
from spack.util.executable import which
#: directory with sample style files
style_data = os.path.join(spack.paths.test_path, "data", "style")

View File

@@ -5,6 +5,7 @@
import argparse
import os
import re
import sys
import pytest
@@ -105,7 +106,7 @@ def test_test_output_on_error(
out = spack_test("run", "test-error", fail_on_error=False)
assert "TestFailure" in out
assert "Command exited with status 1" in out
assert re.search(r"Command '[^\n]*' exited with status 1", out)
def test_test_output_on_failure(
@@ -137,7 +138,7 @@ def test_show_log_on_error(
@pytest.mark.parametrize(
"pkg_name,msgs",
[
("test-error", ["FAILED: Command exited", "TestFailure"]),
("test-error", ["FAILED: Command &#39;", "TestFailure"]),
("test-fail", ["FAILED: Expected", "TestFailure"]),
],
)
@@ -189,7 +190,7 @@ def test_cdash_output_test_error(
report_file = report_dir.join("test-error_Testing.xml")
assert report_file in report_dir.listdir()
content = report_file.open().read()
assert "FAILED: Command exited with status 1" in content
assert re.search(r"FAILED: Command '[^\n]*' exited with status 1", content)
def test_cdash_upload_clean_test(

View File

@@ -10,14 +10,14 @@
import pytest
import llnl.util.envmod
import llnl.util.filesystem as fs
from llnl.util.executable import ProcessError
import spack.compiler
import spack.compilers as compilers
import spack.spec
import spack.util.environment
from spack.compiler import Compiler
from spack.util.executable import ProcessError
@pytest.fixture()
@@ -215,7 +215,7 @@ def call_compiler(exe, *args, **kwargs):
def test_get_compiler_link_paths(monkeypatch, exe, flagname):
# create fake compiler that emits mock verbose output
compiler = MockCompiler()
monkeypatch.setattr(spack.util.executable.Executable, "__call__", call_compiler)
monkeypatch.setattr(llnl.util.executable.Executable, "__call__", call_compiler)
# Grab executable path to test
paths = [getattr(compiler, exe)]
@@ -798,7 +798,7 @@ def module(*args):
def _call(*args, **kwargs):
raise ProcessError("Failed intentionally")
monkeypatch.setattr(spack.util.executable.Executable, "__call__", _call)
monkeypatch.setattr(llnl.util.executable.Executable, "__call__", _call)
# Run and no change to environment
compilers = spack.compilers.get_compilers([compiler_dict])
@@ -868,7 +868,7 @@ class MockPackage(object):
"x86_64",
["/usr/bin/clang", "/usr/bin/clang++", None, None],
)
env = spack.util.environment.EnvironmentModifications()
env = llnl.util.envmod.EnvironmentModifications()
# Check a package that doesn't use xcode and ensure we don't add changes
# to the environment
pkg = MockPackage()
@@ -955,7 +955,7 @@ def test_xcode_not_available(xcode_select_output, mock_executable, monkeypatch):
"x86_64",
["/usr/bin/clang", "/usr/bin/clang++", None, None],
)
env = spack.util.environment.EnvironmentModifications()
env = llnl.util.envmod.EnvironmentModifications()
class MockPackage(object):
use_xcode = True

View File

@@ -24,6 +24,7 @@
import archspec.cpu.microarchitecture
import archspec.cpu.schema
import llnl.util.executable
import llnl.util.lang
import llnl.util.lock
import llnl.util.tty as tty
@@ -45,7 +46,6 @@
import spack.store
import spack.subprocess_context
import spack.test.cray_manifest
import spack.util.executable
import spack.util.git
import spack.util.gpg
import spack.util.spack_yaml as syaml
@@ -511,7 +511,7 @@ def _skip_if_missing_executables(request):
if marker:
required_execs = marker.args
missing_execs = [x for x in required_execs if spack.util.executable.which(x) is None]
missing_execs = [x for x in required_execs if llnl.util.executable.which(x) is None]
if missing_execs:
msg = "could not find executables: {0}"
pytest.skip(msg.format(", ".join(missing_execs)))
@@ -1108,7 +1108,7 @@ def mock_archive(request, tmpdir_factory):
"""Creates a very simple archive directory with a configure script and a
makefile that installs to a prefix. Tars it up into an archive.
"""
tar = spack.util.executable.which("tar")
tar = llnl.util.executable.which("tar")
if not tar:
pytest.skip("requires tar to be installed")
@@ -1168,7 +1168,7 @@ def _parse_cvs_date(line):
@pytest.fixture(scope="session")
def mock_cvs_repository(tmpdir_factory):
"""Creates a very simple CVS repository with two commits and a branch."""
cvs = spack.util.executable.which("cvs", required=True)
cvs = llnl.util.executable.which("cvs", required=True)
tmpdir = tmpdir_factory.mktemp("mock-cvs-repo-dir")
tmpdir.ensure(spack.stage._source_path_subdir, dir=True)
@@ -1460,7 +1460,7 @@ def mock_git_repository(git, tmpdir_factory):
@pytest.fixture(scope="session")
def mock_hg_repository(tmpdir_factory):
"""Creates a very simple hg repository with two commits."""
hg = spack.util.executable.which("hg")
hg = llnl.util.executable.which("hg")
if not hg:
pytest.skip("requires mercurial to be installed")
@@ -1500,11 +1500,11 @@ def mock_hg_repository(tmpdir_factory):
@pytest.fixture(scope="session")
def mock_svn_repository(tmpdir_factory):
"""Creates a very simple svn repository with two commits."""
svn = spack.util.executable.which("svn")
svn = llnl.util.executable.which("svn")
if not svn:
pytest.skip("requires svn to be installed")
svnadmin = spack.util.executable.which("svnadmin", required=True)
svnadmin = llnl.util.executable.which("svnadmin", required=True)
tmpdir = tmpdir_factory.mktemp("mock-svn-stage")
tmpdir.ensure(spack.stage._source_path_subdir, dir=True)
@@ -1897,7 +1897,7 @@ def _factory(rpaths, message="Hello world!"):
message
)
)
gcc = spack.util.executable.which("gcc")
gcc = llnl.util.executable.which("gcc")
executable = source.dirpath("main.x")
# Encode relative RPATHs using `$ORIGIN` as the root prefix
rpaths = [x if os.path.isabs(x) else os.path.join("$ORIGIN", x) for x in rpaths]

View File

@@ -7,12 +7,12 @@
import pytest
from llnl.util.executable import which
from llnl.util.filesystem import mkdirp, touch, working_dir
from spack.fetch_strategy import CvsFetchStrategy
from spack.spec import Spec
from spack.stage import Stage
from spack.util.executable import which
from spack.version import ver
pytestmark = pytest.mark.skipif(not which("cvs"), reason="requires CVS to be installed")

View File

@@ -10,7 +10,7 @@ import sys
import llnl.util.tty as tty
import spack.build_environment
import spack.util.executable
import llnl.util.executable
from spack.package import *
@@ -378,7 +378,7 @@ class Llvm(CMakePackage, CudaPackage):
match = version_regex.search(output)
if match:
return match.group(match.lastindex)
except spack.util.executable.ProcessError:
except llnl.util.executable.ProcessError:
pass
except Exception as e:
tty.debug(e)

View File

@@ -23,6 +23,7 @@
from jsonschema import validate
import llnl.util.lock as lk
from llnl.util.executable import Executable
from llnl.util.tty.colify import colify
import spack.database
@@ -31,7 +32,6 @@
import spack.spec
import spack.store
from spack.schema.database_index import schema
from spack.util.executable import Executable
is_windows = sys.platform == "win32"

View File

@@ -11,6 +11,8 @@
import pytest
from llnl.util.path import path_to_os_path
import spack.paths
import spack.repo
from spack.directory_layout import (
@@ -18,7 +20,6 @@
InvalidDirectoryLayoutParametersError,
)
from spack.spec import Spec
from spack.util.path import path_to_os_path
# number of packages to test (to reduce test time)
max_packages = 10

View File

@@ -8,18 +8,9 @@
import pytest
import spack.util.environment as environment
import llnl.util.envmod as envmod
from spack.paths import spack_root
from spack.util.environment import (
AppendPath,
EnvironmentModifications,
PrependPath,
RemovePath,
SetEnv,
UnsetEnv,
filter_system_paths,
is_system_path,
)
datadir = os.path.join(spack_root, "lib", "spack", "spack", "test", "data")
@@ -43,7 +34,7 @@ def test_inspect_path(tmpdir):
tmpdir.mkdir("lib")
tmpdir.mkdir("include")
env = environment.inspect_path(str(tmpdir), inspections)
env = envmod.inspect_path(str(tmpdir), inspections)
names = [item.name for item in env]
assert "PATH" in names
assert "LIBRARY_PATH" in names
@@ -58,7 +49,7 @@ def test_exclude_paths_from_inspection():
"include": ["CPATH"],
}
env = environment.inspect_path("/usr", inspections, exclude=is_system_path)
env = envmod.inspect_path("/usr", inspections, exclude=envmod.is_system_path)
assert len(env) == 0
@@ -79,7 +70,7 @@ def prepare_environment_for_tests(working_env):
@pytest.fixture
def env(prepare_environment_for_tests):
"""Returns an empty EnvironmentModifications object."""
return EnvironmentModifications()
return envmod.EnvironmentModifications()
@pytest.fixture
@@ -162,7 +153,7 @@ def test_unset(env):
@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
def test_filter_system_paths(miscellaneous_paths):
"""Tests that the filtering of system paths works as expected."""
filtered = filter_system_paths(miscellaneous_paths)
filtered = envmod.filter_system_paths(miscellaneous_paths)
expected = [
"/usr/local/Cellar/gcc/5.3.0/lib",
"/usr/local/opt/some-package/lib",
@@ -246,7 +237,7 @@ def test_extend(env):
"""
env.set("A", "dummy value")
env.set("B", 3)
copy_construct = EnvironmentModifications(env)
copy_construct = envmod.EnvironmentModifications(env)
assert len(copy_construct) == 2
@@ -260,12 +251,12 @@ def test_source_files(files_to_be_sourced):
"""Tests the construction of a list of environment modifications that are
the result of sourcing a file.
"""
env = EnvironmentModifications()
env = envmod.EnvironmentModifications()
for filename in files_to_be_sourced:
if filename.endswith("sourceme_parameters.sh"):
env.extend(EnvironmentModifications.from_sourcing_file(filename, "intel64"))
env.extend(envmod.EnvironmentModifications.from_sourcing_file(filename, "intel64"))
else:
env.extend(EnvironmentModifications.from_sourcing_file(filename))
env.extend(envmod.EnvironmentModifications.from_sourcing_file(filename))
modifications = env.group_by_name()
@@ -277,28 +268,28 @@ def test_source_files(files_to_be_sourced):
# Set new variables
assert len(modifications["NEW_VAR"]) == 1
assert isinstance(modifications["NEW_VAR"][0], SetEnv)
assert isinstance(modifications["NEW_VAR"][0], envmod.SetEnv)
assert modifications["NEW_VAR"][0].value == "new"
assert len(modifications["FOO"]) == 1
assert isinstance(modifications["FOO"][0], SetEnv)
assert isinstance(modifications["FOO"][0], envmod.SetEnv)
assert modifications["FOO"][0].value == "intel64"
# Unset variables
assert len(modifications["EMPTY_PATH_LIST"]) == 1
assert isinstance(modifications["EMPTY_PATH_LIST"][0], UnsetEnv)
assert isinstance(modifications["EMPTY_PATH_LIST"][0], envmod.UnsetEnv)
# Modified variables
assert len(modifications["UNSET_ME"]) == 1
assert isinstance(modifications["UNSET_ME"][0], SetEnv)
assert isinstance(modifications["UNSET_ME"][0], envmod.SetEnv)
assert modifications["UNSET_ME"][0].value == "overridden"
assert len(modifications["PATH_LIST"]) == 3
assert isinstance(modifications["PATH_LIST"][0], RemovePath)
assert isinstance(modifications["PATH_LIST"][0], envmod.RemovePath)
assert modifications["PATH_LIST"][0].value == "/path/third"
assert isinstance(modifications["PATH_LIST"][1], AppendPath)
assert isinstance(modifications["PATH_LIST"][1], envmod.AppendPath)
assert modifications["PATH_LIST"][1].value == "/path/fourth"
assert isinstance(modifications["PATH_LIST"][2], PrependPath)
assert isinstance(modifications["PATH_LIST"][2], envmod.PrependPath)
assert modifications["PATH_LIST"][2].value == "/path/first"
@@ -307,7 +298,7 @@ def test_preserve_environment(prepare_environment_for_tests):
# UNSET_ME is defined, and will be unset in the context manager,
# NOT_SET is not in the environment and will be set within the
# context manager, PATH_LIST is set and will be changed.
with environment.preserve_environment("UNSET_ME", "NOT_SET", "PATH_LIST"):
with envmod.preserve_environment("UNSET_ME", "NOT_SET", "PATH_LIST"):
os.environ["NOT_SET"] = "a"
assert os.environ["NOT_SET"] == "a"
@@ -363,7 +354,7 @@ def test_preserve_environment(prepare_environment_for_tests):
@pytest.mark.usefixtures("prepare_environment_for_tests")
def test_environment_from_sourcing_files(files, expected, deleted):
env = environment.environment_after_sourcing_files(*files)
env = envmod.environment_after_sourcing_files(*files)
# Test that variables that have been modified are still there and contain
# the expected output
@@ -394,7 +385,7 @@ def test_clear(env):
)
def test_sanitize_literals(env, exclude, include):
after = environment.sanitize(env, exclude, include)
after = envmod.sanitize(env, exclude, include)
# Check that all the included variables are there
assert all(x in after for x in include)
@@ -431,7 +422,7 @@ def test_sanitize_literals(env, exclude, include):
)
def test_sanitize_regex(env, exclude, include, expected, deleted):
after = environment.sanitize(env, exclude, include)
after = envmod.sanitize(env, exclude, include)
assert all(x in after for x in expected)
assert all(x not in after for x in deleted)
@@ -442,39 +433,39 @@ def test_sanitize_regex(env, exclude, include, expected, deleted):
"before,after,search_list",
[
# Set environment variables
({}, {"FOO": "foo"}, [environment.SetEnv("FOO", "foo")]),
({}, {"FOO": "foo"}, [envmod.SetEnv("FOO", "foo")]),
# Unset environment variables
({"FOO": "foo"}, {}, [environment.UnsetEnv("FOO")]),
({"FOO": "foo"}, {}, [envmod.UnsetEnv("FOO")]),
# Append paths to an environment variable
(
{"FOO_PATH": "/a/path"},
{"FOO_PATH": "/a/path:/b/path"},
[environment.AppendPath("FOO_PATH", "/b/path")],
[envmod.AppendPath("FOO_PATH", "/b/path")],
),
(
{},
{"FOO_PATH": "/a/path" + os.sep + "/b/path"},
[environment.AppendPath("FOO_PATH", "/a/path" + os.sep + "/b/path")],
[envmod.AppendPath("FOO_PATH", "/a/path" + os.sep + "/b/path")],
),
(
{"FOO_PATH": "/a/path:/b/path"},
{"FOO_PATH": "/b/path"},
[environment.RemovePath("FOO_PATH", "/a/path")],
[envmod.RemovePath("FOO_PATH", "/a/path")],
),
(
{"FOO_PATH": "/a/path:/b/path"},
{"FOO_PATH": "/a/path:/c/path"},
[
environment.RemovePath("FOO_PATH", "/b/path"),
environment.AppendPath("FOO_PATH", "/c/path"),
envmod.RemovePath("FOO_PATH", "/b/path"),
envmod.AppendPath("FOO_PATH", "/c/path"),
],
),
(
{"FOO_PATH": "/a/path:/b/path"},
{"FOO_PATH": "/c/path:/a/path"},
[
environment.RemovePath("FOO_PATH", "/b/path"),
environment.PrependPath("FOO_PATH", "/c/path"),
envmod.RemovePath("FOO_PATH", "/b/path"),
envmod.PrependPath("FOO_PATH", "/c/path"),
],
),
# Modify two variables in the same environment
@@ -482,15 +473,15 @@ def test_sanitize_regex(env, exclude, include, expected, deleted):
{"FOO": "foo", "BAR": "bar"},
{"FOO": "baz", "BAR": "baz"},
[
environment.SetEnv("FOO", "baz"),
environment.SetEnv("BAR", "baz"),
envmod.SetEnv("FOO", "baz"),
envmod.SetEnv("BAR", "baz"),
],
),
],
)
def test_from_environment_diff(before, after, search_list):
mod = environment.EnvironmentModifications.from_environment_diff(before, after)
mod = envmod.EnvironmentModifications.from_environment_diff(before, after)
for item in search_list:
assert item in mod
@@ -501,7 +492,7 @@ def test_from_environment_diff(before, after, search_list):
def test_exclude_lmod_variables():
# Construct the list of environment modifications
file = os.path.join(datadir, "sourceme_lmod.sh")
env = EnvironmentModifications.from_sourcing_file(file)
env = envmod.EnvironmentModifications.from_sourcing_file(file)
# Check that variables related to lmod are not in there
modifications = env.group_by_name()

View File

@@ -8,6 +8,7 @@
import pytest
from llnl.util.executable import which
from llnl.util.filesystem import mkdirp, touch, working_dir
import spack.config
@@ -15,7 +16,6 @@
from spack.fetch_strategy import HgFetchStrategy
from spack.spec import Spec
from spack.stage import Stage
from spack.util.executable import which
from spack.version import ver
# Test functionality covered is supported on Windows, but currently failing

View File

@@ -9,13 +9,13 @@
import pytest
import llnl.util.filesystem as fs
from llnl.util.executable import Executable
import spack.platforms
from spack.hooks.absolutify_elf_sonames import (
SharedLibrariesVisitor,
find_and_patch_sonames,
)
from spack.util.executable import Executable
def skip_unless_linux(f):

View File

@@ -0,0 +1,136 @@
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
"""Test Spack's environment utility functions."""
import os
import sys
import llnl.util.envmod as envmod
is_windows = sys.platform == "win32"
def test_is_system_path():
sys_path = "C:\\Users" if is_windows else "/usr/bin"
assert envmod.is_system_path(sys_path)
assert not envmod.is_system_path("/nonsense_path/bin")
assert not envmod.is_system_path("")
assert not envmod.is_system_path(None)
if is_windows:
test_paths = [
"C:\\Users",
"C:\\",
"C:\\ProgramData",
"C:\\nonsense_path",
"C:\\Program Files",
"C:\\nonsense_path\\extra\\bin",
]
else:
test_paths = [
"/usr/bin",
"/nonsense_path/lib",
"/usr/local/lib",
"/bin",
"/nonsense_path/extra/bin",
"/usr/lib64",
]
def test_filter_system_paths():
nonsense_prefix = "C:\\nonsense_path" if is_windows else "/nonsense_path"
expected = [p for p in test_paths if p.startswith(nonsense_prefix)]
filtered = envmod.filter_system_paths(test_paths)
assert expected == filtered
def deprioritize_system_paths():
expected = [p for p in test_paths if p.startswith("/nonsense_path")]
expected.extend([p for p in test_paths if not p.startswith("/nonsense_path")])
filtered = envmod.deprioritize_system_paths(test_paths)
assert expected == filtered
def test_prune_duplicate_paths():
test_paths = ["/a/b", "/a/c", "/a/b", "/a/a", "/a/c", "/a/a/.."]
expected = ["/a/b", "/a/c", "/a/a", "/a/a/.."]
assert expected == envmod.prune_duplicate_paths(test_paths)
def test_get_path(working_env):
os.environ["TEST_ENV_VAR"] = os.pathsep.join(["/a", "/b", "/c/d"])
expected = ["/a", "/b", "/c/d"]
assert envmod.get_path("TEST_ENV_VAR") == expected
def test_env_flag(working_env):
assert not envmod.env_flag("TEST_NO_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "1"
assert envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "TRUE"
assert envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "True"
assert envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "TRue"
assert envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "true"
assert envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "27"
assert not envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "-2.3"
assert not envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "0"
assert not envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "False"
assert not envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "false"
assert not envmod.env_flag("TEST_ENV_VAR")
os.environ["TEST_ENV_VAR"] = "garbage"
assert not envmod.env_flag("TEST_ENV_VAR")
def test_path_set(working_env):
envmod.path_set("TEST_ENV_VAR", ["/a", "/a/b", "/a/a"])
assert os.environ["TEST_ENV_VAR"] == "/a" + os.pathsep + "/a/b" + os.pathsep + "/a/a"
def test_path_put_first(working_env):
envmod.path_set("TEST_ENV_VAR", test_paths)
expected = ["/usr/bin", "/new_nonsense_path/a/b"]
expected.extend([p for p in test_paths if p != "/usr/bin"])
envmod.path_put_first("TEST_ENV_VAR", expected)
assert envmod.get_path("TEST_ENV_VAR") == expected
def test_reverse_environment_modifications(working_env):
start_env = {
"PREPEND_PATH": os.sep + os.path.join("path", "to", "prepend", "to"),
"APPEND_PATH": os.sep + os.path.join("path", "to", "append", "to"),
"UNSET": "var_to_unset",
"APPEND_FLAGS": "flags to append to",
}
to_reverse = envmod.EnvironmentModifications()
to_reverse.prepend_path("PREPEND_PATH", "/new/path/prepended")
to_reverse.append_path("APPEND_PATH", "/new/path/appended")
to_reverse.set_path("SET_PATH", ["/one/set/path", "/two/set/path"])
to_reverse.set("SET", "a var")
to_reverse.unset("UNSET")
to_reverse.append_flags("APPEND_FLAGS", "more_flags")
reversal = to_reverse.reversed()
os.environ = start_env.copy()
print(os.environ)
to_reverse.apply_modifications()
print(os.environ)
reversal.apply_modifications()
print(os.environ)
start_env.pop("UNSET")
assert os.environ == start_env

View File

@@ -0,0 +1,20 @@
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import sys
import llnl.util.path
def test_sanitze_file_path(tmpdir):
"""Test filtering illegal characters out of potential file paths"""
# *nix illegal files characters are '/' and none others
illegal_file_path = str(tmpdir) + "//" + "abcdefghi.txt"
if sys.platform == "win32":
# Windows has a larger set of illegal characters
illegal_file_path = os.path.join(tmpdir, 'a<b>cd?e:f"g|h*i.txt')
real_path = llnl.util.path.sanitize_file_path(illegal_file_path)
assert real_path == os.path.join(str(tmpdir), "abcdefghi.txt")

View File

@@ -3,7 +3,7 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from spack.util.string import plural
from llnl.util.string import plural
def test_plural():

View File

@@ -19,8 +19,7 @@
import llnl.util.lang as lang
import llnl.util.tty.log as log
import llnl.util.tty.pty as pty
from spack.util.executable import which
from llnl.util.executable import which
termios: Optional[ModuleType] = None
try:

View File

@@ -7,10 +7,10 @@
import pytest
import llnl.util.executable as exe
import llnl.util.filesystem as fs
import spack.paths
import spack.util.executable as exe
import spack.util.git
from spack.main import get_version, main

View File

@@ -16,8 +16,9 @@
import pytest
from llnl.util.envmod import path_put_first
from spack.build_environment import MakeExecutable
from spack.util.environment import path_put_first
pytestmark = pytest.mark.skipif(
sys.platform == "win32",

View File

@@ -9,16 +9,15 @@
import pytest
from llnl.util.executable import which
from llnl.util.filesystem import resolve_link_target_relative_to_the_link
import spack.mirror
import spack.repo
import spack.util.executable
import spack.util.spack_json as sjson
import spack.util.url as url_util
from spack.spec import Spec
from spack.stage import Stage
from spack.util.executable import which
from spack.util.spack_yaml import SpackYAMLError
pytestmark = [

Some files were not shown because too many files have changed in this diff Show More