From 42a998bba2443660876582f2b8a23296e077fb57 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Thu, 7 May 2026 09:48:25 +0200 Subject: [PATCH 1/4] Add vs2026 locations As of May 2026, GHA uses new windows-2025 runners that have vs2026 installed (instead of vs2022) (closes #112) --- cue.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cue.py b/cue.py index f7bbee9..e16e4a4 100644 --- a/cue.py +++ b/cue.py @@ -286,6 +286,8 @@ def __exit__(self,A,B,C): vcvars_table = { # https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#History + 'vs2026': [r'C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat', + r'C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Auxiliary\Build\vcvarsall.bat'], 'vs2022': [r'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat', r'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat'], 'vs2019': [r'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat', From 48d71d9fe0ba692d128ca9e0d01546bafbcf770b Mon Sep 17 00:00:00 2001 From: ralphlange <632191+ralphlange@users.noreply.github.com> Date: Tue, 14 Jul 2026 20:25:21 +0200 Subject: [PATCH 2/4] Use vswhere for compiler detection of 'vs' setting - Use the 'vswhere' tool to automatically resolve the Visual Studio compiler location when the generic 'vs' compiler setting is specified. - Fall back to the hardcoded list of locations only if vswhere is not available on the system. - If a single installation is found, auto-select it; if multiple versions are found, print the options and raise a ValueError. Co-authored-by: google-labs-jules[bot] --- cue-test.py | 100 +++++++++++++++++++++++++++++++++++ cue.py | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 245 insertions(+), 2 deletions(-) diff --git a/cue-test.py b/cue-test.py index 8e5d84e..9ef6820 100644 --- a/cue-test.py +++ b/cue-test.py @@ -342,6 +342,106 @@ def test_Repos(self): self.assertEqual(repo_access(mod), 0, 'Defaults for {0} do not point to a valid git repository at {1}' .format(mod, cue.setup[mod + '_REPOURL'])) +class TestVSCompilerResolution(unittest.TestCase): + def setUp(self): + cue.clear_lists() + # Save original state of variables we will mock + self.orig_ci = dict(cue.ci) + self.orig_vcvars_found = dict(cue.vcvars_found) + self.orig_get_vs_installations_via_vswhere = cue.get_vs_installations_via_vswhere + + def tearDown(self): + # Restore original state + cue.ci.clear() + cue.ci.update(self.orig_ci) + cue.vcvars_found.clear() + cue.vcvars_found.update(self.orig_vcvars_found) + cue.get_vs_installations_via_vswhere = self.orig_get_vs_installations_via_vswhere + + def test_vswhere_single_installation(self): + cue.ci['os'] = 'windows' + cue.ci['compiler'] = 'vs' + + mock_installs = [{ + 'path': r'C:\Program Files\Microsoft Visual Studio\2022\Community', + 'vcvarsall': r'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat', + 'version': '17.4.33103.184', + 'name': 'Visual Studio Community 2022' + }] + cue.get_vs_installations_via_vswhere = lambda: mock_installs + + cue.resolve_vs_compiler() + + self.assertEqual(cue.ci['compiler'], 'vs2022') + self.assertEqual(cue.vcvars_found['vs2022'], mock_installs[0]['vcvarsall']) + + def test_vswhere_multiple_installations(self): + cue.ci['os'] = 'windows' + cue.ci['compiler'] = 'vs' + + mock_installs = [ + { + 'path': r'C:\Program Files\Microsoft Visual Studio\2022\Community', + 'vcvarsall': r'C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat', + 'version': '17.4.33103.184', + 'name': 'Visual Studio Community 2022' + }, + { + 'path': r'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community', + 'vcvarsall': r'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat', + 'version': '16.5.30011.22', + 'name': 'Visual Studio Community 2019' + } + ] + cue.get_vs_installations_via_vswhere = lambda: mock_installs + + with self.assertRaisesRegex(ValueError, "Multiple Visual Studio installations found"): + cue.resolve_vs_compiler() + + def test_vswhere_no_installations(self): + cue.ci['os'] = 'windows' + cue.ci['compiler'] = 'vs' + + cue.get_vs_installations_via_vswhere = lambda: [] + + with self.assertRaisesRegex(ValueError, "No Visual Studio installations found"): + cue.resolve_vs_compiler() + + def test_fallback_single_installation(self): + cue.ci['os'] = 'windows' + cue.ci['compiler'] = 'vs' + + cue.get_vs_installations_via_vswhere = lambda: None + cue.vcvars_found = {'vs2019': r'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat'} + + cue.resolve_vs_compiler() + + self.assertEqual(cue.ci['compiler'], 'vs2019') + + def test_fallback_multiple_installations(self): + cue.ci['os'] = 'windows' + cue.ci['compiler'] = 'vs' + + cue.get_vs_installations_via_vswhere = lambda: None + cue.vcvars_found = { + 'vs2019': r'C:\vcvars19.bat', + 'vs2022': r'C:\vcvars22.bat' + } + + with self.assertRaisesRegex(ValueError, "Multiple Visual Studio installations found"): + cue.resolve_vs_compiler() + + def test_fallback_no_installations(self): + cue.ci['os'] = 'windows' + cue.ci['compiler'] = 'vs' + + cue.get_vs_installations_via_vswhere = lambda: None + cue.vcvars_found = {} + + with self.assertRaisesRegex(ValueError, "No Visual Studio installations found"): + cue.resolve_vs_compiler() + + @unittest.skipIf(ci_os != 'windows', 'VCVars test only applies to windows') class TestVCVars(unittest.TestCase): def test_vcvars(self): diff --git a/cue.py b/cue.py index e16e4a4..f529448 100644 --- a/cue.py +++ b/cue.py @@ -70,7 +70,7 @@ def detect_context(): ci['platform'] = 'x64' ci['compiler'] = os.environ['TRAVIS_COMPILER'] ci['choco'] += ['strawberryperl'] - if re.match(r'^vs', ci['compiler']): + if re.match(r'^vs', ci['compiler']) and ci['compiler'] != 'vs': # Only Visual Studio 2017 available ci['compiler'] = 'vs2017' if 'BCFG' in os.environ: @@ -165,12 +165,13 @@ def detect_context(): if 'CLEAN_DEPS' in os.environ and os.environ['CLEAN_DEPS'].lower() == 'no': ci['clean_deps'] = False + resolve_vs_compiler() + logger.debug('Detected a build hosted on %s, using %s on %s (%s) configured as %s ' + '(test: %s, clean_deps: %s)', ci['service'], ci['compiler'], ci['os'], ci['platform'], ci['configuration'], ci['test'], ci['clean_deps']) - curdir = os.getcwd() ci = {} @@ -309,6 +310,148 @@ def __exit__(self,A,B,C): vcvars_found[key] = dir +def find_vswhere(): + if hasattr(shutil, 'which'): + loc = shutil.which('vswhere') + if loc: + return loc + for path in [ + r'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe', + r'C:\Program Files\Microsoft Visual Studio\Installer\vswhere.exe' + ]: + if os.path.exists(path): + return path + return None + + +def query_vswhere(): + import json + vswhere_path = find_vswhere() + if not vswhere_path: + return None + try: + cmd = [vswhere_path, '-legacy', '-prerelease', '-format', 'json', '-products', '*'] + logger.debug("Running vswhere: %s", ' '.join(cmd)) + output = sp.check_output(cmd).decode('utf-8', errors='ignore') + return json.loads(output) + except Exception as e: + logger.debug("vswhere execution or parsing failed: %s", e) + try: + cmd = [vswhere_path, '-legacy', '-prerelease', '-format', 'json'] + output = sp.check_output(cmd).decode('utf-8', errors='ignore') + return json.loads(output) + except Exception as e2: + logger.debug("vswhere fallback failed: %s", e2) + return None + + +def find_vcvarsall_in_install(install_path): + paths = [ + os.path.join(install_path, r'VC\Auxiliary\Build\vcvarsall.bat'), + os.path.join(install_path, r'VC\vcvarsall.bat') + ] + for p in paths: + if os.path.exists(p): + return p + return None + + +def get_vs_installations_via_vswhere(): + instances = query_vswhere() + if instances is None: + return None + + valid_installs = [] + for inst in instances: + path = inst.get('installationPath') + if not path: + continue + vcvars_path = find_vcvarsall_in_install(path) + if vcvars_path: + version_str = inst.get('installationVersion', '') + name = inst.get('displayName', inst.get('installationName', 'Visual Studio')) + valid_installs.append({ + 'path': path, + 'vcvarsall': vcvars_path, + 'version': version_str, + 'name': name + }) + return valid_installs + + +def resolve_vs_compiler(): + if ci['os'] != 'windows' or ci['compiler'] != 'vs': + return + + installs = get_vs_installations_via_vswhere() + if installs is not None: + if len(installs) == 1: + inst = installs[0] + major_version_str = inst['version'].split('.')[0] + try: + major_version = int(major_version_str) + except ValueError: + major_version = 0 + + VS_VERSION_MAP = { + 15: 'vs2017', + 16: 'vs2019', + 17: 'vs2022', + 18: 'vs2026', + 14: 'vs2015', + 12: 'vs2013', + 11: 'vs2012', + 10: 'vs2010', + 9: 'vs2008' + } + vs_key = VS_VERSION_MAP.get(major_version, 'vs' + major_version_str) + + ci['compiler'] = vs_key + vcvars_found[vs_key] = inst['vcvarsall'] + print("Selected installed Visual Studio compiler: {0} ({1}) at {2}".format(vs_key, inst['name'], inst['vcvarsall'])) + sys.stdout.flush() + elif len(installs) > 1: + print("Multiple Visual Studio installations found:") + for inst in installs: + major_version_str = inst['version'].split('.')[0] + try: + major = int(major_version_str) + except ValueError: + major = 0 + VS_VERSION_MAP = { + 15: 'vs2017', + 16: 'vs2019', + 17: 'vs2022', + 18: 'vs2026', + 14: 'vs2015', + 12: 'vs2013', + 11: 'vs2012', + 10: 'vs2010', + 9: 'vs2008' + } + vs_key = VS_VERSION_MAP.get(major, 'vs' + major_version_str) + print(" {0}: {1} at {2}".format(vs_key, inst['name'], inst['path'])) + sys.stdout.flush() + raise ValueError("Multiple Visual Studio installations found. Please specify one of the available options.") + else: + raise ValueError("No Visual Studio installations found on this system.") + else: + valid_keys = list(vcvars_found.keys()) + if len(valid_keys) == 1: + vs_key = valid_keys[0] + ci['compiler'] = vs_key + print("Selected installed Visual Studio compiler (via fallback list): {0} at {1}".format(vs_key, vcvars_found[vs_key])) + sys.stdout.flush() + elif len(valid_keys) > 1: + print("Multiple Visual Studio installations found in fallback list:") + for k in valid_keys: + print(" {0}: {1}".format(k, vcvars_found[k])) + sys.stdout.flush() + raise ValueError("Multiple Visual Studio installations found. Please specify one of the available options.") + else: + raise ValueError("No Visual Studio installations found in fallback list.") + + def modlist(): if building_base: ret = [] From 85fddd78783828a3e96864eee59b3fe498ed537e Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 14 Jul 2026 20:28:35 +0200 Subject: [PATCH 3/4] test: fix TestVCVars test for Windows-2025 runners with vs2026 --- cue-test.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cue-test.py b/cue-test.py index 9ef6820..d6ba3aa 100644 --- a/cue-test.py +++ b/cue-test.py @@ -449,10 +449,15 @@ def test_vcvars(self): os.environ['TRAVIS_COMPILER'] = 'vs2017' else: os.environ['CONFIGURATION'] = 'default' - if ci_service == 'github-actions' and os.environ['IMAGEOS'] == 'win16': - os.environ['CMP'] = 'vs2017' - elif ci_service == 'github-actions' and os.environ['IMAGEOS'] == 'win19': - os.environ['CMP'] = 'vs2019' + if ci_service == 'github-actions': + if os.environ['IMAGEOS'] == 'win16': + os.environ['CMP'] = 'vs2017' + elif os.environ['IMAGEOS'] == 'win19': + os.environ['CMP'] = 'vs2019' + elif os.environ['IMAGEOS'] == 'win25-vs2026': + os.environ['CMP'] = 'vs2026' + else: + os.environ['CMP'] = 'vs2022' else: os.environ['CMP'] = 'vs2022' cue.detect_context() From 53ce73136c76b5e0bd0a3f6154b0ba1a722eec48 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 14 Jul 2026 17:15:03 +0200 Subject: [PATCH 4/4] ci: switch windows builds to use 'vs' --- .github/workflows/build-and-test.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 31b8026..1adc9a8 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -125,21 +125,22 @@ jobs: fail-fast: false matrix: os: [windows-2025, windows-2022] - cmp: [gcc, vs2022] + cmp: [gcc, vs] configuration: [default, static, debug, static-debug] base: [ "7.0" ] include: - os: windows-2025 - cmp: vs2022 + cmp: vs configuration: static base: "3.15" - name: "B-3.15 Win-25 MSC-22 static" + name: "B-3.15 Win-25 MSC-26 static" - os: windows-2025 - cmp: vs2022 + cmp: vs configuration: static base: "3.14" - name: "B-3.14 Win-25 MSC-22 static" + name: "B-3.14 Win-25 MSC-26 static" + steps: - uses: actions/checkout@v7 - name: Prepare and compile dependencies