From 7f3678ab1823057a46e3d6c9eb3ba375bb8e8110 Mon Sep 17 00:00:00 2001 From: Nathan Rusch Date: Thu, 20 Jun 2024 17:04:21 -0700 Subject: [PATCH] Improve PowerShell plugin's profile handling functionality * Add support for the `package_commands_sourced_first` config switch. Prior to this change, the rez PowerShell implementation would always source the rez context script after the user/host profile scripts, so profile-level modifications to environment variables like `PATH` were always squashed by the unconditional overrides in the context script. With this change, the relative source order of the shell profile vs. the context script can be properly controlled using the `package_commands_sourced_first` config option, matching the behavior of `SH`-based shells. NOTE: Because this config option defaults to True, this commit also implicitly changes rez's default behavior to source the shell profile *after* the context when using PowerShell. * Support the `norc` shell plugin option, which enables the use of `rez-env --norc` to bypass the sourcing of any profile scripts. Signed-off-by: Nathan Rusch --- .../shell/_utils/powershell_base.py | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/rezplugins/shell/_utils/powershell_base.py b/src/rezplugins/shell/_utils/powershell_base.py index f059bb248..10dcbf5c4 100644 --- a/src/rezplugins/shell/_utils/powershell_base.py +++ b/src/rezplugins/shell/_utils/powershell_base.py @@ -23,6 +23,15 @@ class PowerShellBase(Shell): expand_env_vars = True syspaths = None + # PowerShell does not provide built-in functionality to manually trigger + # sourcing of the user/host profile scripts in the appropriate order, so we + # have to implement it ourselves in order to be able to control the relative + # source order of the user's profile vs. the generated rez context script. + profile_source_cmd = ( + '$PROFILE ' + '| Get-Member -type NoteProperty ' + '| ForEach-Object { if (Test-Path $PROFILE."$($_.Name)") { . $PROFILE."$($_.Name)" }}') + # Make sure that the $Env:VAR formats come before the $VAR formats since # PowerShell Environment variables are ambiguous with Unix paths. # Note: This is used in other parts of Rez @@ -49,10 +58,8 @@ def startup_capabilities(cls, stdin=False, command=False): cls._unsupported_option('rcfile', rcfile) - cls._unsupported_option('norc', norc) cls._unsupported_option('stdin', stdin) rcfile = False - norc = False stdin = False return (rcfile, norc, stdin, command) @@ -131,14 +138,23 @@ def spawn_shell(self, quiet=False, pre_command=None, add_rez=True, + package_commands_sourced_first=None, **Popen_args): startup_sequence = self.get_startup_sequence(rcfile, norc, bool(stdin), command) shell_command = None + if package_commands_sourced_first is None: + package_commands_sourced_first = config.package_commands_sourced_first + def _record_shell(ex, files, bind_rez=True, print_msg=False): - ex.source(context_file) + if package_commands_sourced_first: + ex.source(context_file) + if not norc: + ex.command(self.profile_source_cmd) + if not package_commands_sourced_first: + ex.source(context_file) if startup_sequence["envvar"]: ex.unsetenv(startup_sequence["envvar"]) if add_rez and bind_rez: @@ -198,8 +214,14 @@ def _record_shell(ex, files, bind_rez=True, print_msg=False): cmd += [self.executable] - # Suppresses copyright message of PowerShell and pwsh - cmd += ["-NoLogo"] + # `-NoLogo` suppresses copyright message of PowerShell and pwsh. + # `-NoProfile` skips automatic sourcing of user/host profile scripts. + # We manually inject the profile sourcing before or after the sourcing + # of the generated context script based on the value of + # `package_commands_sourced_first` (see `_record_shell` above). + cmd += ["-NoLogo", "-NoProfile"] + if shell_command is None: + cmd += ["-NoExit"] # Powershell execution policy overrides # Prevent injections/mistakes by ensuring policy value only contains letters. @@ -210,9 +232,6 @@ def _record_shell(ex, files, bind_rez=True, print_msg=False): # Generic form of sourcing that works in powershell and pwsh cmd += ["-File", target_file] - if shell_command is None: - cmd.insert(1, "-NoExit") - p = Popen(cmd, env=env, **Popen_args) return p