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:
		@@ -164,7 +164,7 @@ def setup_custom_environment(self, pkg, env):
 | 
				
			|||||||
            out = out.decode("utf-16le", errors="replace")  # novermin
 | 
					            out = out.decode("utf-16le", errors="replace")  # novermin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int_env = dict(
 | 
					        int_env = dict(
 | 
				
			||||||
            (key.lower(), value)
 | 
					            (key, value)
 | 
				
			||||||
            for key, _, value in (line.partition("=") for line in out.splitlines())
 | 
					            for key, _, value in (line.partition("=") for line in out.splitlines())
 | 
				
			||||||
            if key and value
 | 
					            if key and value
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
import re
 | 
					import re
 | 
				
			||||||
import socket
 | 
					import socket
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					from functools import wraps
 | 
				
			||||||
from typing import Any, Callable, Dict, List, MutableMapping, Optional, Tuple, Union
 | 
					from typing import Any, Callable, Dict, List, MutableMapping, Optional, Tuple, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from llnl.util import tty
 | 
					from llnl.util import tty
 | 
				
			||||||
@@ -83,6 +84,28 @@ def double_quote_escape(s):
 | 
				
			|||||||
    return '"' + s.replace('"', r"\"") + '"'
 | 
					    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:
 | 
					def is_system_path(path: Path) -> bool:
 | 
				
			||||||
    """Returns True if the argument is a system path, False otherwise."""
 | 
					    """Returns True if the argument is a system path, False otherwise."""
 | 
				
			||||||
    return bool(path) and (os.path.normpath(path) in SYSTEM_DIRS)
 | 
					    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)
 | 
					        return Trace(filename=filename, lineno=lineno, context=current_context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def set(self, name: str, value: str, *, force: bool = False):
 | 
					    def set(self, name: str, value: str, *, force: bool = False):
 | 
				
			||||||
        """Stores a request to set an environment variable.
 | 
					        """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)
 | 
					        item = SetEnv(name, value, trace=self._trace(), force=force)
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def append_flags(self, name: str, value: str, sep: str = " "):
 | 
					    def append_flags(self, name: str, value: str, sep: str = " "):
 | 
				
			||||||
        """Stores a request to append 'flags' to an environment variable.
 | 
					        """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())
 | 
					        item = AppendFlagsEnv(name, value, separator=sep, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def unset(self, name: str):
 | 
					    def unset(self, name: str):
 | 
				
			||||||
        """Stores a request to unset an environment variable.
 | 
					        """Stores a request to unset an environment variable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -497,6 +523,7 @@ def unset(self, name: str):
 | 
				
			|||||||
        item = UnsetEnv(name, trace=self._trace())
 | 
					        item = UnsetEnv(name, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def remove_flags(self, name: str, value: str, sep: str = " "):
 | 
					    def remove_flags(self, name: str, value: str, sep: str = " "):
 | 
				
			||||||
        """Stores a request to remove flags from an environment variable
 | 
					        """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())
 | 
					        item = RemoveFlagsEnv(name, value, separator=sep, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def set_path(self, name: str, elements: List[str], separator: str = os.pathsep):
 | 
					    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,
 | 
					        """Stores a request to set an environment variable to a list of paths,
 | 
				
			||||||
        separated by a character defined in input.
 | 
					        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())
 | 
					        item = SetPath(name, elements, separator=separator, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def append_path(self, name: str, path: str, separator: str = os.pathsep):
 | 
					    def append_path(self, name: str, path: str, separator: str = os.pathsep):
 | 
				
			||||||
        """Stores a request to append a path to list of paths.
 | 
					        """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())
 | 
					        item = AppendPath(name, path, separator=separator, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def prepend_path(self, name: str, path: str, separator: str = os.pathsep):
 | 
					    def prepend_path(self, name: str, path: str, separator: str = os.pathsep):
 | 
				
			||||||
        """Stores a request to prepend a path to list of paths.
 | 
					        """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())
 | 
					        item = PrependPath(name, path, separator=separator, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def remove_path(self, name: str, path: str, separator: str = os.pathsep):
 | 
					    def remove_path(self, name: str, path: str, separator: str = os.pathsep):
 | 
				
			||||||
        """Stores a request to remove a path from a list of paths.
 | 
					        """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())
 | 
					        item = RemovePath(name, path, separator=separator, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def deprioritize_system_paths(self, name: str, separator: str = os.pathsep):
 | 
					    def deprioritize_system_paths(self, name: str, separator: str = os.pathsep):
 | 
				
			||||||
        """Stores a request to deprioritize system paths in a path list,
 | 
					        """Stores a request to deprioritize system paths in a path list,
 | 
				
			||||||
        otherwise preserving the order.
 | 
					        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())
 | 
					        item = DeprioritizeSystemPaths(name, separator=separator, trace=self._trace())
 | 
				
			||||||
        self.env_modifications.append(item)
 | 
					        self.env_modifications.append(item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @system_env_normalize
 | 
				
			||||||
    def prune_duplicate_paths(self, name: str, separator: str = os.pathsep):
 | 
					    def prune_duplicate_paths(self, name: str, separator: str = os.pathsep):
 | 
				
			||||||
        """Stores a request to remove duplicates from a path list, otherwise
 | 
					        """Stores a request to remove duplicates from a path list, otherwise
 | 
				
			||||||
        preserving the order.
 | 
					        preserving the order.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user