Windows: shell variables are case-insensitive (#36813)

If we modify both Path and PATH, on Windows they will clobber one
another. This PR updates the shell modification logic to automatically
convert variable names to upper-case on Windows.
This commit is contained in:
John W. Parent 2023-04-21 14:38:58 -04:00 committed by GitHub
parent 255c9ed5e9
commit d8451b0c3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 1 deletions

View File

@ -164,7 +164,7 @@ def setup_custom_environment(self, pkg, env):
out = out.decode("utf-16le", errors="replace") # novermin
int_env = dict(
(key.lower(), value)
(key, value)
for key, _, value in (line.partition("=") for line in out.splitlines())
if key and value
)

View File

@ -14,6 +14,7 @@
import re
import socket
import sys
from functools import wraps
from typing import Any, Callable, Dict, List, MutableMapping, Optional, Tuple, Union
from llnl.util import tty
@ -83,6 +84,28 @@ def double_quote_escape(s):
return '"' + s.replace('"', r"\"") + '"'
def system_env_normalize(func):
"""Decorator wrapping calls to system env modifications,
converting all env variable names to all upper case on Windows, no-op
on other platforms before calling env modification method.
Windows, due to a DOS holdover, treats all env variable names case
insensitively, however Spack's env modification class does not,
meaning setting `Path` and `PATH` would be distinct env operations
for Spack, but would cause a collision when actually performing the
env modification operations on the env.
Normalize all env names to all caps to prevent this collision from the
Spack side."""
@wraps(func)
def case_insensitive_modification(self, name: str, *args, **kwargs):
if sys.platform == "win32":
name = name.upper()
return func(self, name, *args, **kwargs)
return case_insensitive_modification
def is_system_path(path: Path) -> bool:
"""Returns True if the argument is a system path, False otherwise."""
return bool(path) and (os.path.normpath(path) in SYSTEM_DIRS)
@ -466,6 +489,7 @@ def _trace(self) -> Optional[Trace]:
return Trace(filename=filename, lineno=lineno, context=current_context)
@system_env_normalize
def set(self, name: str, value: str, *, force: bool = False):
"""Stores a request to set an environment variable.
@ -477,6 +501,7 @@ def set(self, name: str, value: str, *, force: bool = False):
item = SetEnv(name, value, trace=self._trace(), force=force)
self.env_modifications.append(item)
@system_env_normalize
def append_flags(self, name: str, value: str, sep: str = " "):
"""Stores a request to append 'flags' to an environment variable.
@ -488,6 +513,7 @@ def append_flags(self, name: str, value: str, sep: str = " "):
item = AppendFlagsEnv(name, value, separator=sep, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def unset(self, name: str):
"""Stores a request to unset an environment variable.
@ -497,6 +523,7 @@ def unset(self, name: str):
item = UnsetEnv(name, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def remove_flags(self, name: str, value: str, sep: str = " "):
"""Stores a request to remove flags from an environment variable
@ -508,6 +535,7 @@ def remove_flags(self, name: str, value: str, sep: str = " "):
item = RemoveFlagsEnv(name, value, separator=sep, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def set_path(self, name: str, elements: List[str], separator: str = os.pathsep):
"""Stores a request to set an environment variable to a list of paths,
separated by a character defined in input.
@ -520,6 +548,7 @@ def set_path(self, name: str, elements: List[str], separator: str = os.pathsep):
item = SetPath(name, elements, separator=separator, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def append_path(self, name: str, path: str, separator: str = os.pathsep):
"""Stores a request to append a path to list of paths.
@ -531,6 +560,7 @@ def append_path(self, name: str, path: str, separator: str = os.pathsep):
item = AppendPath(name, path, separator=separator, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def prepend_path(self, name: str, path: str, separator: str = os.pathsep):
"""Stores a request to prepend a path to list of paths.
@ -542,6 +572,7 @@ def prepend_path(self, name: str, path: str, separator: str = os.pathsep):
item = PrependPath(name, path, separator=separator, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def remove_path(self, name: str, path: str, separator: str = os.pathsep):
"""Stores a request to remove a path from a list of paths.
@ -553,6 +584,7 @@ def remove_path(self, name: str, path: str, separator: str = os.pathsep):
item = RemovePath(name, path, separator=separator, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def deprioritize_system_paths(self, name: str, separator: str = os.pathsep):
"""Stores a request to deprioritize system paths in a path list,
otherwise preserving the order.
@ -564,6 +596,7 @@ def deprioritize_system_paths(self, name: str, separator: str = os.pathsep):
item = DeprioritizeSystemPaths(name, separator=separator, trace=self._trace())
self.env_modifications.append(item)
@system_env_normalize
def prune_duplicate_paths(self, name: str, separator: str = os.pathsep):
"""Stores a request to remove duplicates from a path list, otherwise
preserving the order.