From d8451b0c3f9ded4c7703fdefb102371138a4189c Mon Sep 17 00:00:00 2001 From: "John W. Parent" <45471568+johnwparent@users.noreply.github.com> Date: Fri, 21 Apr 2023 14:38:58 -0400 Subject: 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. --- lib/spack/spack/compilers/msvc.py | 2 +- lib/spack/spack/util/environment.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/spack/spack/compilers/msvc.py b/lib/spack/spack/compilers/msvc.py index 061555a500..87e6e42af4 100644 --- a/lib/spack/spack/compilers/msvc.py +++ b/lib/spack/spack/compilers/msvc.py @@ -164,7 +164,7 @@ class Msvc(Compiler): 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 ) diff --git a/lib/spack/spack/util/environment.py b/lib/spack/spack/util/environment.py index 64082ff313..0eed2726b4 100644 --- a/lib/spack/spack/util/environment.py +++ b/lib/spack/spack/util/environment.py @@ -14,6 +14,7 @@ import platform 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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 @@ class EnvironmentModifications: 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. -- cgit v1.2.3-60-g2f50