Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,32 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Whatever John Doe did.

From Joseph Brill:
- MSVS: Fix a significant MSVC/MSVC tool initialization slowdown when
vcpkg has been installed and the PSModulePath is not initialized
or propagated from the user's shell environment. To resolve this
The default Windows Powershell 7 path is added before the default
Windows Powershell 5 path in the carefully constructed
environment in which the MSVC batch files are run. The shell
environment variables and values for VCPKG_DISABLE_METRICS and
VCPKG_ROOT are added to the limited MSVC environment when defined.
At present, the VCPKG_DISABLE_METRICS and VCPKG_ROOT variables and
values are not propagated to the SCons environment after running
the MSVC batch files.
- MSVC: A significant delay was experienced in the windows 2022
and 2025 runners due to the limited environment in which the MSVC
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add "GitHub Actions runners', instead of runners?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked all text.

batch files are invoked. The vcpkg component installed in Visual
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If the vcpkg component is installed with Visual Studio installler"... ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked all text.

Studio invokes a powershell script when the MSVC batch files are
called. The limited MSVC environment 1) did not have the preferred
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

".. environment used by SCons to initialized MSVC"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked all text.

pwsh executable on the system path, 2) did not include the powershell
module analysis cache location (PSModuleAnalysisCachePath), and 3) did
not include the powershell module path (PSModulePath) which appears to
be responsible for the performance degradation in the windows 2022 and
2025 runners. The VS vcpkg component is not installed in the windows
2019 runner. Adding the pwsh and powershell executable paths in the
order discovered on the shell environment path, passing the powershell
module analysis cache location, and adding a limited subset of the
powershell module path appears to have eliminated the significant
delays. In the windows runners, any one of the these three changes
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

".. Github Actions windows runners.."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked all text.

appears to eliminite the significant delays. It is hoped that the
combination of all three changes will guard against significant
delays in other environment configurations as well.
- MSVC: The following shell variables and values are passed to the
environment in which the MSVC batch files are invoked:
Copy link
Copy Markdown
Contributor

@bdbaddog bdbaddog Jun 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"environment used by SCons to initialize MSVC"..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked all text.

VCPKG_DISABLE_METRICS, VCPKG_ROOT, POWERSHELL_TELEMETRY_OPTOUT,
PSDisableModuleAnalysisCacheCleanup, and PSModuleAnalysisCachePath.
A limited subset of the shell PSModulePath environment variable value
is passed to the MSVC environment. None of these variables and values
are propagated to the SCons environment after running the MSVC batch
files.

From Bill Prendergast:
- Fixed SCons.Variables.PackageVariable to correctly test the default
Expand Down
17 changes: 12 additions & 5 deletions RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,24 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY
one from PEP 308 introduced in Python 2.5 (2006). The idiom being
replaced (using and/or) is regarded as error prone.

- MSVS: The default Windows powershell 7 path is added before the default
Windows powershell 5 path in the limited environment in which the
MSVC batch files are run.
- MSVC: The following shell environment variables are now included in
the environment in which the MSVC batch files are invoked when defined:
VCPKG_DISABLE_METRICS, VCPKG_ROOT, POWERSHELL_TELEMETRY_OPTOUT,
PSDisableModuleAnalysisCacheCleanup, and PSModuleAnalysisCachePath.
A subset of the shell environment PSModulePath path elements is
included in the limited environment in which the MSVC batch files
are invoked.

FIXES
-----

- Fixed SCons.Variables.PackageVariable to correctly test the default
setting against both enable & disable strings. (Fixes #4702)
- MSVS: Fix significant slowdown initializing MSVC tools when vcpkg has
been installed on the system.

- MSVC: Fixed a significant delay that was experienced in the windows
2022 and 2025 runners due to the limited environment in which the MSVC
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Github Actions windows..."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked all text.

batch files are invoked. The windows 2019 runner was not affected.

- Fix the variant dir component being missing from generated source file
paths with CompilationDatabase() builder (Fixes #4003).

Expand Down
126 changes: 110 additions & 16 deletions SCons/Tool/MSCommon/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import os
import re
import sys
import time
from contextlib import suppress
from subprocess import DEVNULL, PIPE
from pathlib import Path
Expand Down Expand Up @@ -70,6 +71,9 @@
'windir', # windows directory (SystemRoot not available in 95/98/ME)
'VCPKG_DISABLE_METRICS',
'VCPKG_ROOT',
'POWERSHELL_TELEMETRY_OPTOUT',
'PSDisableModuleAnalysisCacheCleanup',
'PSModuleAnalysisCachePath',
]

class MSVCCacheInvalidWarning(SCons.Warnings.WarningOnByDefault):
Expand Down Expand Up @@ -339,6 +343,89 @@ def _force_vscmd_skip_sendtelemetry(env):
return True


class _Powershell:

_PSEXECUTABLES = (
"pwsh.exe",
"powershell.exe",
)

_PSMODULEPATH_MAP = {os.path.normcase(os.path.abspath(p)): p for p in [
# os.path.expandvars(r"%USERPROFILE%\Documents\PowerShell\Modules"), # current user
os.path.expandvars(r"%ProgramFiles%\PowerShell\Modules"), # all users
os.path.expandvars(r"%ProgramFiles%\PowerShell\7\Modules"), # installation location
# os.path.expandvars(r"%USERPROFILE%\Documents\WindowsPowerShell\Modules"), # current user
os.path.expandvars(r"%ProgramFiles%\WindowsPowerShell\Modules"), # all users
os.path.expandvars(r"%windir%\System32\WindowsPowerShell\v1.0\Modules"), # installation location
]}

_cache_norm_path = {}

@classmethod
def _get_norm_path(cls, p):
norm_path = cls._cache_norm_path.get(p)
if norm_path is None:
norm_path = os.path.normcase(os.path.abspath(p))
cls._cache_norm_path[p] = norm_path
cls._cache_norm_path[norm_path] = norm_path
return norm_path

_cache_is_psmodulepath = {}

@classmethod
def _is_psmodulepath(cls, p):
is_psmodulepath = cls._cache_is_psmodulepath.get(p)
if is_psmodulepath is None:
norm_path = cls._get_norm_path(p)
is_psmodulepath = bool(norm_path in cls._PSMODULEPATH_MAP)
cls._cache_is_psmodulepath[p] = is_psmodulepath
cls._cache_is_psmodulepath[norm_path] = is_psmodulepath
return is_psmodulepath

_cache_psmodulepath = {}

@classmethod
def get_psmodulepath(cls, pathspec):
psmodulepath = cls._cache_psmodulepath.get(pathspec)
if psmodulepath is None:
psmodulepath_paths = []
for p in pathspec.split(os.pathsep):
p = p.strip()
if not p:
continue
if not cls._is_psmodulepath(p):
continue
psmodulepath_paths.append(p)
psmodulepath = os.pathsep.join(psmodulepath_paths)
cls._cache_psmodulepath[pathspec] = psmodulepath
return psmodulepath

_cache_psexe_paths = {}

@classmethod
def get_psexe_paths(cls, pathspec):
psexe_paths = cls._cache_psexe_paths.get(pathspec)
if psexe_paths is None:
psexe_set = set(cls._PSEXECUTABLES)
psexe_paths = []
for p in pathspec.split(os.pathsep):
p = p.strip()
if not p:
continue
for psexe in psexe_set:
psexe_path = os.path.join(p, psexe)
if not os.path.exists(psexe_path):
continue
psexe_paths.append(p)
psexe_set.remove(psexe)
break
if psexe_set:
continue
break
cls._cache_psexe_paths[pathspec] = tuple(psexe_paths)
return psexe_paths


def normalize_env(env, keys, force: bool=False):
"""Given a dictionary representing a shell environment, add the variables
from os.environ needed for the processing of .bat files; the keys are
Expand Down Expand Up @@ -379,29 +466,31 @@ def normalize_env(env, keys, force: bool=False):
if sys32_wbem_dir not in normenv['PATH']:
normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_wbem_dir

# ProgramFiles for PowerShell 7 Path and PSModulePath
progfiles_dir = os.environ.get("ProgramFiles")
if not progfiles_dir:
sysroot_drive, _ = os.path.splitdrive(sys32_dir)
sysroot_path = sysroot_drive + os.sep
progfiles_dir = os.path.join(sysroot_path, "Program Files")

# Powershell 7
progfiles_ps_dir = os.path.join(progfiles_dir, "PowerShell", "7")
if progfiles_ps_dir not in normenv["PATH"]:
normenv["PATH"] = normenv["PATH"] + os.pathsep + progfiles_ps_dir

# Without Powershell in PATH, an internal call to a telemetry
# function (starting with a VS2019 update) can fail
# Note can also set VSCMD_SKIP_SENDTELEMETRY to avoid this.
sys32_ps_dir = os.path.join(sys32_dir, r'WindowsPowerShell\v1.0')
if sys32_ps_dir not in normenv['PATH']:
normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_ps_dir

# Find the powershell executable paths. Add the known powershell.exe
# path to the end of the shell system path (just in case).
# The VS vcpkg component prefers pwsh.exe if it's on the path.
sys32_ps_dir = os.path.join(sys32_dir, 'WindowsPowerShell', 'v1.0')
psexe_searchlist = os.pathsep.join([os.environ.get("PATH", ""), sys32_ps_dir])
psexe_pathlist = _Powershell.get_psexe_paths(psexe_searchlist)

# Add powershell executable paths in the order discovered.
for psexe_path in psexe_pathlist:
if psexe_path not in normenv['PATH']:
normenv['PATH'] = normenv['PATH'] + os.pathsep + psexe_path

debug("PATH: %s", normenv['PATH'])
return normenv

# Add psmodulepath paths in the order discovered.
psmodulepath = _Powershell.get_psmodulepath(os.environ.get("PSModulePath", ""))
if psmodulepath:
normenv["PSModulePath"] = psmodulepath

debug("PSModulePath: %s", normenv.get('PSModulePath',''))
return normenv


def get_output(vcbat, args=None, env=None, skip_sendtelemetry=False):
Expand All @@ -425,10 +514,15 @@ def get_output(vcbat, args=None, env=None, skip_sendtelemetry=False):
debug("Calling '%s'", vcbat)
cmd_str = '"%s" & set' % vcbat

beg_time = time.time()

cp = SCons.Action.scons_subproc_run(
env, cmd_str, stdin=DEVNULL, stdout=PIPE, stderr=PIPE,
)

end_time = time.time()
debug("Elapsed %.2fs", end_time - beg_time)

# Extra debug logic, uncomment if necessary
# debug('stdout:%s', cp.stdout)
# debug('stderr:%s', cp.stderr)
Expand Down
Loading