Commit d685e3f7 authored by Nirbheek Chauhan's avatar Nirbheek Chauhan 🐜
Browse files

cerbero: Allow specifying a custom location for Visual Studio

Sometimes Visual Studio is installed into a custom location, or the
user wants to specifically select a particular Visual Studio
installation when there are many installed.

You can set the `vs_install_path` and `vs_install_version` config
variables to override the automatic detection of the Visual Studio
installation location. F.ex., in `~/.cerbero/cerbero.cbc`

This is needed for running Cerbero on the Windows CI, where we use
Visual Studio Build Tools 2019 installed in `C:\BuildTools`.
parent 2437e4de
......@@ -29,7 +29,7 @@ from cerbero.errors import FatalError, ConfigurationError
from cerbero.utils import _, system_info, validate_packager, shell
from cerbero.utils import to_unixpath, to_winepath, parse_file, detect_qt5
from cerbero.utils import messages as m
from cerbero.ide.vs.env import get_vs_version
from cerbero.ide.vs.env import get_vs_year_version
CONFIG_EXT = 'cbc'
......@@ -132,7 +132,8 @@ class Config (object):
'meson_cross_properties', 'manifest', 'extra_properties',
'qt5_qmake_path', 'qt5_pkgconfigdir', 'for_shell',
'package_tarball_compression', 'extra_mirrors',
'extra_bootstrap_packages', 'moltenvk_prefix']
'extra_bootstrap_packages', 'moltenvk_prefix',
'vs_install_path', 'vs_install_version']
cookbook = None
......@@ -259,7 +260,9 @@ class Config (object):
if self.can_use_msvc():
m.message('Building recipes with Visual Studio {} whenever possible'
if self.vs_install_path:
m.message('Using Visual Studio installed at {!r}'.format(self.vs_install_path))
# Store current os.environ data
for c in list(self.arch_config.values()):
......@@ -663,6 +666,9 @@ class Config (object):
target_platform = self.target_platform
if target_platform == Platform.WINDOWS and 'visualstudio' in self.variants:
target_platform = 'msvc'
# Check for invalid configuration of a custom Visual Studio path
if self.vs_install_path and not self.vs_install_version:
raise ConfigurationError('vs_install_path was set, but vs_install_version was not')
self.set_property('prefix', os.path.join(self.home_dir, "dist",
"%s_%s" % (target_platform, self.target_arch)))
self.set_property('sources', os.path.join(self.home_dir, "sources",
......@@ -25,16 +25,32 @@ from cerbero.enums import Architecture
from cerbero.errors import FatalError
# We only support Visual Studio 2015 as of now
vcvarsalls = {
'vs14': [r'Microsoft Visual Studio 14.0\VC\vcvarsall.bat'],
'vs15': [r'Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat',
r'Microsoft Visual Studio\2017\Professional\VC\Auxiliary\Build\vcvarsall.bat',
r'Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat'],
'vs16': [r'Microsoft Visual Studio\2019\Preview\VC\Auxiliary\Build\vcvarsall.bat',
r'Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat',
r'Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvarsall.bat',
r'Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat',
r'Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvarsall.bat'],
'vs14': (
r'Microsoft Visual Studio 14.0',
'vs15': (
r'Microsoft Visual Studio\2017\Community',
r'Microsoft Visual Studio\2017\Professional',
r'Microsoft Visual Studio\2017\Enterprise',
r'Microsoft Visual Studio\2017\Preview',
'vs16': (
r'Microsoft Visual Studio\2019\Community',
r'Microsoft Visual Studio\2019\Professional',
r'Microsoft Visual Studio\2019\Enterprise',
r'Microsoft Visual Studio\2019\BuildTools',
r'Microsoft Visual Studio\2019\Preview',
def get_program_files_dir():
......@@ -46,7 +62,7 @@ def get_program_files_dir():
return Path(os.environ['PROGRAMFILES'])
raise FatalError('Could not find path to 32-bit Program Files directory')
def get_vs_version(vcver):
def get_vs_year_version(vcver):
if vcver == 'vs14':
return '2015'
if vcver == 'vs15':
......@@ -55,20 +71,43 @@ def get_vs_version(vcver):
return '2019'
raise RuntimeError('Unknown toolset value {!r}'.format(vcver))
def get_vcvarsall(version=None):
if version is not None:
versions = [version]
def _get_custom_vs_install(vs_version, vs_install_path):
path = Path(vs_install_path)
if not path.is_dir():
raise FatalError('vs_install_path {!r} is not a directory'.format(path))
suffix = VCVARSALLS[vs_version][1]
path = path / suffix
if not path.is_file():
raise FatalError('Can\'t find vcvarsall.bat inside vs_install_path {!r}'.format(path))
return path.as_posix(), vs_version
def get_vcvarsall(vs_version, vs_install_path):
known_vs_versions = sorted(VCVARSALLS.keys(), reverse=True)
if vs_version:
if vs_version not in VCVARSALLS:
raise FatalError('Requested Visual Studio version {} is not one of: '
'{}'.format(vs_version, ', '.join(known_vs_versions)))
# Do we want to use a specific known Visual Studio installation?
if vs_install_path:
return _get_custom_vs_install(vs_version, vs_install_path)
# Start searching.
if vs_version:
vs_versions = [vs_version]
versions = sorted(vcvarsalls.keys(), reverse=True)
# If no specific version was requested, look for all known versions and
# pick the newest one found.
vs_versions = known_vs_versions
program_files = get_program_files_dir()
for version in versions:
for path in vcvarsalls[version]:
path = program_files / path
for vs_version in vs_versions:
prefixes, suffix = VCVARSALLS[vs_version]
for prefix in prefixes:
path = program_files / prefix / suffix
# Find the location of the Visual Studio installation
if path.is_file():
return path.as_posix(), version
raise FatalError('Microsoft Visual Studio not found, please file a bug. '
'We looked for: ' + ', '.join(versions))
return path.as_posix(), vs_version
raise FatalError('Microsoft Visual Studio not found. If you installed it, '
'please file a bug. We looked for: ' + ', '.join(versions))
def append_path(var, path, sep=';'):
if var and not var.endswith(sep):
......@@ -122,9 +161,9 @@ def get_envvar_msvc_values(msvc, nomsvc, sep=';'):
return msvc[0:index]
def get_msvc_env(arch, target_arch, version=None):
def get_msvc_env(arch, target_arch, version=None, vs_install_path=None):
ret_env = {}
vcvarsall, vsver = get_vcvarsall(version)
vcvarsall, vsver = get_vcvarsall(version, vs_install_path)
without_msvc = run_and_get_env('set')
arg = get_vcvarsall_arg(arch, target_arch)
......@@ -115,7 +115,8 @@ class GenLib(object):
if platform != Platform.WINDOWS:
return None, None
from cerbero.ide.vs.env import get_msvc_env
msvc_env = get_msvc_env('x86', target_arch)[0]
msvc_env = get_msvc_env('x86', target_arch, self.config.vs_install_version,
paths = msvc_env['PATH']
return shutil.which('lib', path=paths), paths
......@@ -118,7 +118,7 @@ if platform == Platform.WINDOWS:
# that were appended/prepended and have new values
# FIXME: Use EnvVarOp class from cerbero/build/
msvc_toolchain_env = {}
env, msvc_version = get_msvc_env(arch, target_arch)
env, msvc_version = get_msvc_env(arch, target_arch, vs_install_version, vs_install_path)
for key, value in env.items():
if key in ('PATH', 'PATHEXT', 'INCLUDE', 'LIB'):
sep = separator
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment