From 00a2b506a2324296dd3b292ff9b5f0702d66f9b0 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 12 Jun 2025 09:11:56 +0200 Subject: [PATCH] third_party: Update waf to version 2.1.6 Signed-off-by: Andreas Schneider Reviewed-by: Anoop C S Autobuild-User(master): Anoop C S Autobuild-Date(master): Thu Jun 12 13:39:03 UTC 2025 on atb-devel-224 --- buildtools/bin/waf | 2 +- buildtools/wafsamba/wafsamba.py | 2 +- third_party/waf/waflib/Context.py | 6 +- third_party/waf/waflib/Tools/msvc.py | 4 +- third_party/waf/waflib/extras/msvc_toolset.py | 348 ++++++++++++++++++ 5 files changed, 355 insertions(+), 7 deletions(-) create mode 100755 third_party/waf/waflib/extras/msvc_toolset.py diff --git a/buildtools/bin/waf b/buildtools/bin/waf index c531761461a..b7a315ac672 100755 --- a/buildtools/bin/waf +++ b/buildtools/bin/waf @@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. import os, sys, inspect -VERSION="2.1.5" +VERSION="2.1.6" REVISION="x" GIT="x" INSTALL="x" diff --git a/buildtools/wafsamba/wafsamba.py b/buildtools/wafsamba/wafsamba.py index 2d106a0722c..8dff3d6bf1b 100644 --- a/buildtools/wafsamba/wafsamba.py +++ b/buildtools/wafsamba/wafsamba.py @@ -40,7 +40,7 @@ LIB_PATH="shared" os.environ['PYTHONUNBUFFERED'] = '1' -if Context.HEXVERSION not in (0x2010500,): +if Context.HEXVERSION not in (0x2010600,): Logs.error(''' Please use the version of waf that comes with Samba, not a system installed version. See http://wiki.samba.org/index.php/Waf diff --git a/third_party/waf/waflib/Context.py b/third_party/waf/waflib/Context.py index f3d8ebffd1c..edc6d442272 100644 --- a/third_party/waf/waflib/Context.py +++ b/third_party/waf/waflib/Context.py @@ -18,13 +18,13 @@ else: import imp # the following 3 constants are updated on each new release (do not touch) -HEXVERSION=0x2010500 +HEXVERSION=0x2010600 """Constant updated on new releases""" -WAFVERSION="2.1.5" +WAFVERSION="2.1.6" """Constant updated on new releases""" -WAFREVISION="5360d9c9c2623d1799fe8b0ce35581ff0925993a" +WAFREVISION="aeec9358394f883f63b3ad78c2857117ca2cdfb4" """Git revision when the waf version is updated""" WAFNAME="waf" diff --git a/third_party/waf/waflib/Tools/msvc.py b/third_party/waf/waflib/Tools/msvc.py index 218aee1a84c..d4d6f584308 100644 --- a/third_party/waf/waflib/Tools/msvc.py +++ b/third_party/waf/waflib/Tools/msvc.py @@ -109,10 +109,10 @@ class MSVCVersion(object): m = re.search(r'^(.*)\s+(\d+[.]\d+)', ver) if m: self.name = m.group(1) - self.number = float(m.group(2)) + self.number = tuple(int(i) for i in m.group(2).split('.')) else: self.name = ver - self.number = 0. + self.number = tuple() def __lt__(self, other): if self.number == other.number: diff --git a/third_party/waf/waflib/extras/msvc_toolset.py b/third_party/waf/waflib/extras/msvc_toolset.py new file mode 100755 index 00000000000..3a63ae3e9a7 --- /dev/null +++ b/third_party/waf/waflib/extras/msvc_toolset.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +"""Tool used to discover Visual Studio side-by-side (SxS) installations. + +Implements an alternative discovery mechanism for Visual Studio 2017 and +newer to support SxS toolsets and specific Windows SDK releases. This acts +as a replacement for msvc.py to allow developers to pin to specific Visual +Studio and Windows SDK releases. + +Usage:: + + $ waf configure --msvc_version='msvc 17.9' --winsdk-version='8.1' + +or:: + + def configure(ctx): + ctx.env.MSVC_VERSIONS = ['msvc 17.9'] + ctx.env.WINSDK_VERSION = '8.1' + ctx.load('msvc_toolset') +""" + +import contextlib +import functools +import glob +import inspect +import json +import logging +import os +import re +import subprocess +import tempfile + +try: # noqa: SIM105 + from waflib import Configure, Context, Options +except ImportError: + pass + + +@functools.total_ordering +class _VisualStudioToolset(object): # noqa: UP004 + """Class used to represent a Visual Studio toolset installation.""" + + VS_INSTALL_ROOT = '{}\\Microsoft Visual Studio'.format( + os.environ.get('PROGRAMFILES(X86)') + or os.environ.get('PROGRAMFILES') + or 'C:\\Program Files (x86)', + ) + + _discovered = None + + @classmethod + def discover(cls, log_debug, runner=None): # noqa: ANN001, ANN206 + """Memoize the Visual Studio toolsets installed. + + :param log_debug: Python logging.Logger.debug method + :type log_debug: Callable[[str], None] + :param runner: Method used to invoke a subprocess + :type runner: Callable[[list(str)], str], optional + :return: collection of toolsets installed + :rtype: list[_VisualStudioToolset] + """ + if cls._discovered is None: + run_process = runner or functools.partial( + subprocess.check_output, + universal_newlines=True, + ) + listing = run_process( + [ + '{}/Installer/vswhere.exe'.format(cls.VS_INSTALL_ROOT), # noqa: UP032 + '-all', + '-products', + '*', + '-format', + 'json', + '-utf8', + '-include', + 'packages', + ], + ) + + installations = json.loads(listing) + + vc_components = '{}(?:{})'.format( + re.escape('Microsoft.VisualStudio.Component.VC.'), + '|'.join( # noqa: FLY002 + [ + r'Tools\.(?!14)', + '140', + 'v141', + r'(?:Tools\.)?14(?:\.\d+)+', + ], + ), + ) + re_sxs_ids = re.compile(vc_components) + + vc_components = ( + '{}:{}'.format(install['installationVersion'], package['id']) + for install in installations + for package in install['packages'] + if re_sxs_ids.match(package['id']) + ) + + for component in vc_components: + log_debug(component) + + sxs_minimum_version = 15 + installs = ( + (install['installationVersion'], install['installationPath']) + for install in installations + if next(int(i) for i in install['installationVersion'].split('.')) + >= sxs_minimum_version + ) + + toolset_dirs = { + cls(install_version, toolset_dir) + for install_version, install_path in installs + for toolset_dir in glob.iglob('{}/VC/Tools/MSVC/*'.format(install_path)) # noqa: PTH207, UP032 + if os.path.isdir('{}/bin'.format(toolset_dir)) # noqa: PTH112, UP032 + } + + cls._discovered = sorted(toolset_dirs) + + return cls._discovered + + def __init__(self, install_version, directory): # noqa: ANN001, ANN204 + """Construct an instance of the class. + + :param install_version: Visual Studio version containing the toolset + :type install_version: str + :param directory: Fully qualified path to the toolset directory + :type directory: os.pathLike + """ + self.install_version = install_version + self._path = directory + self._vcvars_ver = os.path.basename(directory) # noqa: PTH119 + self._vcvarsall = os.path.normpath( + '{}/../../../Auxiliary/Build/vcvarsall.bat'.format(directory), # noqa: UP032 + ) + + version = tuple(int(i) for i in self._vcvars_ver.split('.')) + # https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170 + if version < (14, 20): + self._visual_studio_version = (15, version[1] - 7) + elif version < (14, 30): + self._visual_studio_version = ( + 16, + version[1] + - 20 + # 16.9 distinguished from 16.8 by build number + # this also accounts for the minor version not + # being updated for 16.10 + + bool(version >= (14, 28, 29910)) + # 16.11 distinguished from 16.10 by build number + + bool(version >= (14, 29, 30129)), + ) + else: + self._visual_studio_version = (17, version[1] - 30) + + def __eq__(self, other): # noqa: ANN001, ANN204 + """Return if the instance is equal to another instance of this class. + + :param other: Another toolset to compare against + :type other: _VisualStudioToolset + :return: True if the toolsets represent the same Visual Studio version, False otherwise + :rtype: bool + """ + return self._visual_studio_version == other._visual_studio_version + + def __lt__(self, other): # noqa: ANN001, ANN204 + """Return if the instance is less than another instance of this class. + + :param other: Another toolset to compare against + :type other: _VisualStudioToolset + :return: True if the toolsets represent an older Visual Studio version, False otherwise + :rtype: bool + """ + return self._visual_studio_version < other._visual_studio_version + + def __hash__(self): # noqa: ANN204 + """Utilize _path member as class hash. + + :return: Python hash of the toolset directory + :rtype: int + """ + return hash(self._path) + + def __str__(self): # noqa: ANN204 + """Obtain the string representation of the class. + + :return: A uniquely identified string representation for the toolset + :rtype: str + """ + return '{:<16s} {:<6s} {}'.format( + '{}:'.format(self.install_version), # noqa: UP032 + '{}:'.format(self.visual_studio_version), # noqa: UP032 + self.vc_version, + ) + + @property + def visual_studio_version(self): # noqa: ANN202 + """Obtain the Visual Studio version associated with this toolset. + + :return: Visual Studio major and minor version. + :rtype: str + """ + return '{}.{}'.format(*self._visual_studio_version) + + @property + def vcvarsall(self): # noqa: ANN202 + """Obtain the configuration script. + + :return: Fully qualified path to vcvarsall.bat + :rtype: str + """ + return self._vcvarsall + + @property + def vc_version(self): # noqa: ANN202 + """Obtain the vc_version option. + + :return: vc_version used to configure toolset in vcvarsall.bat + :rtype: str + """ + return self._vcvars_ver + + def cl(self): # noqa: ANN202 + """Run and return the compiler output. + + :return: CL output on success, or vcvarsall output on error + :rtype: list(str) + """ + contents = inspect.cleandoc( + """ + @echo off + call "{}" amd64 -vcvars_ver={} + if not "%ERRORLEVEL%"=="0" exit /b %ERRORLEVEL% + cl + """, + ).format(self.vcvarsall, self._vcvars_ver) + + with tempfile.NamedTemporaryFile('w', suffix='.cmd', delete=False) as tmp: + try: + tmp.write(contents) + tmp.close() + process = subprocess.Popen( # noqa: S603 + [tmp.name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + shell=False, + ) + stdout, stderr = process.communicate() + if process.returncode: + return stdout.splitlines() + stderr.splitlines() + return stderr.splitlines() + finally: + os.unlink(tmp.name) # noqa: PTH108 + + +def options(ctx): # noqa: ANN001, ANN201 + """Configure the Microsoft compiler(s) Waf options. + + :param ctx: Waf context associated with option handling + :type ctx: waflib.Options.OptionContext + """ + ctx.load('msvc') + + winsdk_default = re.findall( + r'(8\.1|10(?:\.\d+)+)', + os.environ.get('WINDOWSSDKVERSION', ''), + ) or [''] + + ctx.add_option( + '--winsdk-version', + type=str, + help='Windows SDK Versions, eg: 10.0.19041.0', + default=winsdk_default[0], + ) + + +@contextlib.contextmanager +def _use_vs_toolsets_discovery(): # noqa: ANN202 + """Patch the Waf msvc tools VS2017+ discovery.""" + msvc = Context.load_tool('msvc') + + def gather_vswhere_versions(ctx, versions): # noqa: ANN001, ANN202 + """Detect the Microsoft SxS compiler(s) for Waf. + + :param ctx: Waf context associated with tool configuration + :type ctx: waflib.Configure.ConfigurationContext + :param versions: The upstream msvc versions dictionary + :type versions: dict + """ + msvc.gather_vswhere_versions(ctx, versions) + log_debug = functools.partial( + logging.getLogger('waflib').debug, + extra={'zone': 'msvc_toolset'}, + ) + + for toolset in _VisualStudioToolset.discover(log_debug, ctx.cmd_and_log): + waf_version = 'msvc {}'.format(toolset.visual_studio_version) # noqa: UP032 + if waf_version in versions: + log_debug("Replacing msvc's '%s' with msvc_toolset's '%s'", waf_version, toolset) + versions[waf_version] = { + target: msvc.target_compiler( + ctx, + 'msvc', + realtarget, + toolset.visual_studio_version, + '{} {} -vcvars_ver={}'.format( + target, + getattr(Options.options, 'winsdk_version', '') + or ctx.env.WINSDK_VERSION + or '', + toolset.vc_version, + ), + toolset.vcvarsall, + ) + for target, realtarget in msvc.all_msvc_platforms[::-1] + } + + try: + Configure.conf(gather_vswhere_versions) + yield + finally: + Configure.conf(msvc.gather_vswhere_versions) + + +def configure(ctx): # noqa: ANN001, ANN201 + """Configure the Microsoft compiler(s) in Waf. + + :param ctx: Waf context associated with tool configuration + :type ctx: waflib.Configure.ConfigurationContext + """ + with _use_vs_toolsets_discovery(): + ctx.load('msvc') + + +if __name__ == '__main__': + logger = logging.getLogger('msvc_toolset') + logging.basicConfig( + format='%(message)s', + level=getattr(logging, os.environ.get('LOG_LEVEL', 'info').upper()), + ) + + for toolset in _VisualStudioToolset.discover(logger.debug): + logger.info(toolset) + cl_info = '\n '.join(line for line in toolset.cl() if line) + logger.info(' %s', cl_info) -- 2.47.2