Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
11 changes: 6 additions & 5 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
113 changes: 109 additions & 4 deletions cue-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,17 +342,122 @@ 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):
if ci_service == 'travis':
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()
Expand Down
149 changes: 147 additions & 2 deletions cue.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -286,6 +287,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',
Expand All @@ -307,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 = []
Expand Down
Loading