Merge pull request #1614 from shoover/lazy-msvc-detect-1.8

Optionally lazily check installed msvc compilers' environments
This commit is contained in:
ita1024 2015-09-01 07:27:36 +02:00
commit 36e07b3eaf
1 changed files with 113 additions and 28 deletions

View File

@ -30,6 +30,10 @@ or::
Platforms and targets will be tested in the order they appear;
the first good configuration will be used.
To skip testing all the configurations that are not used, use the ``--msvc_lazy_autodetect`` option
or set ``conf.env['MSVC_LAZY_AUTODETECT']=True``.
Supported platforms: ia64, x64, x86, x86_amd64, x86_ia64, x86_arm, amd64_x86, amd64_arm
Compilers supported:
@ -90,8 +94,19 @@ all_icl_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'),
def options(opt):
opt.add_option('--msvc_version', type='string', help = 'msvc version, eg: "msvc 10.0,msvc 9.0"', default='')
opt.add_option('--msvc_targets', type='string', help = 'msvc targets, eg: "x64,arm"', default='')
opt.add_option('--msvc_lazy_autodetect', action='store_true', help = 'lazily check msvc target environments')
def setup_msvc(conf, versions, arch = False):
"""
Checks installed compilers and targets and returns the first combination from the user's
options, env, or the global supported lists that checks.
:param versions: A list of tuples of all installed compilers and available targets.
:param arch: Whether to return the target architecture.
:return: the compiler, revision, path, include dirs, library paths, and (optionally) target architecture
:rtype: tuple of strings
"""
platforms = getattr(Options.options, 'msvc_targets', '').split(',')
if platforms == ['']:
platforms=Utils.to_list(conf.env['MSVC_TARGETS']) or [i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms]
@ -102,15 +117,20 @@ def setup_msvc(conf, versions, arch = False):
for version in desired_versions:
try:
targets = dict(versiondict [version])
targets = dict(versiondict[version])
for target in platforms:
try:
arch,(p1,p2,p3) = targets[target]
compiler,revision = version.rsplit(' ', 1)
if arch:
return compiler,revision,p1,p2,p3,arch
try:
arch,(p1,p2,p3) = targets[target]
except conf.errors.ConfigurationError:
# lazytup target evaluation errors
del(targets[target])
else:
return compiler,revision,p1,p2,p3
compiler,revision = version.rsplit(' ', 1)
if arch:
return compiler,revision,p1,p2,p3,arch
else:
return compiler,revision,p1,p2,p3
except KeyError: continue
except KeyError: continue
conf.fatal('msvc: Impossible to find a valid architecture for building (in setup_msvc)')
@ -118,13 +138,14 @@ def setup_msvc(conf, versions, arch = False):
@conf
def get_msvc_version(conf, compiler, version, target, vcvars):
"""
Create a bat file to obtain the location of the libraries
Checks that an installed compiler actually runs and uses vcvars to obtain the
environment needed by the compiler.
:param compiler: ?
:param version: ?
:target: ?
:vcvars: ?
:return: the location of msvc, the location of include dirs, and the library paths
:param compiler: compiler type, for looking up the executable name
:param version: compiler version, for debugging only
:param target: target architecture
:param vcvars: batch file to run to check the environment
:return: the location of the compiler executable, the location of include dirs, and the library paths
:rtype: tuple of strings
"""
debug('msvc: get_msvc_version: %r %r %r', compiler, version, target)
@ -219,7 +240,7 @@ def gather_wsdk_versions(conf, versions):
targets = []
for target,arch in all_msvc_platforms:
try:
targets.append((target, (arch, conf.get_msvc_version('wsdk', version, '/'+target, os.path.join(path, 'bin', 'SetEnv.cmd')))))
targets.append((target, (arch, get_compiler_env(conf, 'wsdk', version, '/'+target, os.path.join(path, 'bin', 'SetEnv.cmd')))))
except conf.errors.ConfigurationError:
pass
versions.append(('wsdk ' + version[1:], targets))
@ -304,6 +325,66 @@ def gather_msvc_detected_versions():
detected_versions.sort(key = fun)
return detected_versions
def get_compiler_env(conf, compiler, version, bat_target, bat, select=None):
"""
Gets the compiler environment variables as a tuple. Evaluation is eager by default.
If set to lazy with ``--msvc_lazy_autodetect`` or ``env.MSVC_LAZY_AUTODETECT``
the environment is evaluated when the tuple is destructured or iterated. This means
destructuring can throw :py:class:`conf.errors.ConfigurationError`.
:param conf: configuration context to use to eventually get the version environment
:param compiler: compiler name
:param version: compiler version number
:param bat: path to the batch file to run
:param select: optional function to take the realized environment variables tup and map it (e.g. to combine other constant paths)
"""
lazy = getattr(Options.options, 'msvc_lazy_autodetect', False) or conf.env['MSVC_LAZY_AUTODETECT']
def msvc_thunk():
vs = conf.get_msvc_version(compiler, version, bat_target, bat)
if select:
return select(vs)
else:
return vs
return lazytup(msvc_thunk, lazy, ([], [], []))
class lazytup(object):
"""
A tuple that evaluates its elements from a function when iterated or destructured.
:param fn: thunk to evaluate the tuple on demand
:param lazy: whether to delay evaluation or evaluate in the constructor
:param default: optional default for :py:func:`repr` if it should not evaluate
"""
def __init__(self, fn, lazy=True, default=None):
self.fn = fn
self.default = default
if not lazy:
self.evaluate()
def __len__(self):
self.evaluate()
return len(self.value)
def __iter__(self):
self.evaluate()
for i, v in enumerate(self.value):
yield v
def __getitem__(self, i):
self.evaluate()
return self.value[i]
def __repr__(self):
if hasattr(self, 'value'):
return repr(self.value)
elif self.default:
return repr(self.default)
else:
self.evaluate()
return repr(self.value)
def evaluate(self):
if hasattr(self, 'value'):
return
self.value = self.fn()
@conf
def gather_msvc_targets(conf, versions, version, vc_path):
#Looking for normal MSVC compilers!
@ -311,17 +392,17 @@ def gather_msvc_targets(conf, versions, version, vc_path):
if os.path.isfile(os.path.join(vc_path, 'vcvarsall.bat')):
for target,realtarget in all_msvc_platforms[::-1]:
try:
targets.append((target, (realtarget, conf.get_msvc_version('msvc', version, target, os.path.join(vc_path, 'vcvarsall.bat')))))
targets.append((target, (realtarget, get_compiler_env(conf, 'msvc', version, target, os.path.join(vc_path, 'vcvarsall.bat')))))
except conf.errors.ConfigurationError:
pass
elif os.path.isfile(os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat')):
try:
targets.append(('x86', ('x86', conf.get_msvc_version('msvc', version, 'x86', os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat')))))
targets.append(('x86', ('x86', get_compiler_env(conf, 'msvc', version, 'x86', os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat')))))
except conf.errors.ConfigurationError:
pass
elif os.path.isfile(os.path.join(vc_path, 'Bin', 'vcvars32.bat')):
try:
targets.append(('x86', ('x86', conf.get_msvc_version('msvc', version, '', os.path.join(vc_path, 'Bin', 'vcvars32.bat')))))
targets.append(('x86', ('x86', get_compiler_env(conf, 'msvc', version, '', os.path.join(vc_path, 'Bin', 'vcvars32.bat')))))
except conf.errors.ConfigurationError:
pass
if targets:
@ -336,15 +417,19 @@ def gather_wince_targets(conf, versions, version, vc_path, vsvars, supported_pla
winCEpath = os.path.join(vc_path, 'ce')
if not os.path.isdir(winCEpath):
continue
try:
common_bindirs,_1,_2 = conf.get_msvc_version('msvc', version, 'x86', vsvars)
except conf.errors.ConfigurationError:
continue
if os.path.isdir(os.path.join(winCEpath, 'lib', platform)):
bindirs = [os.path.join(winCEpath, 'bin', compiler), os.path.join(winCEpath, 'bin', 'x86_'+compiler)] + common_bindirs
bindirs = [os.path.join(winCEpath, 'bin', compiler), os.path.join(winCEpath, 'bin', 'x86_'+compiler)]
incdirs = [os.path.join(winCEpath, 'include'), os.path.join(winCEpath, 'atlmfc', 'include'), include]
libdirs = [os.path.join(winCEpath, 'lib', platform), os.path.join(winCEpath, 'atlmfc', 'lib', platform), lib]
cetargets.append((platform, (platform, (bindirs,incdirs,libdirs))))
def combine_common(compiler_env):
(common_bindirs,_1,_2) = compiler_env
return (bindirs + common_bindirs, incdirs, libdirs)
try:
cetargets.append((platform, (platform, get_compiler_env(conf, 'msvc', version, 'x86', vsvars, combine_common))))
except conf.errors.ConfigurationError:
continue
if cetargets:
versions.append((device + ' ' + version, cetargets))
@ -354,8 +439,8 @@ def gather_winphone_targets(conf, versions, version, vc_path, vsvars):
targets = []
for target,realtarget in all_msvc_platforms[::-1]:
try:
targets.append((target, (realtarget, conf.get_msvc_version('winphone', version, target, vsvars))))
except conf.errors.ConfigurationError as e:
targets.append((target, (realtarget, get_compiler_env(conf, 'winphone', version, target, vsvars))))
except conf.errors.ConfigurationError:
pass
if targets:
versions.append(('winphone '+ version, targets))
@ -426,7 +511,7 @@ def gather_icl_versions(conf, versions):
batch_file=os.path.join(path,'bin','iclvars.bat')
if os.path.isfile(batch_file):
try:
targets.append((target,(arch,conf.get_msvc_version('intel',version,target,batch_file))))
targets.append((target,(arch,get_compiler_env(conf,'intel',version,target,batch_file))))
except conf.errors.ConfigurationError:
pass
except WindowsError:
@ -438,7 +523,7 @@ def gather_icl_versions(conf, versions):
batch_file=os.path.join(path,'bin','iclvars.bat')
if os.path.isfile(batch_file):
try:
targets.append((target, (arch, conf.get_msvc_version('intel', version, target, batch_file))))
targets.append((target, (arch, get_compiler_env(conf, 'intel', version, target, batch_file))))
except conf.errors.ConfigurationError:
pass
except WindowsError:
@ -490,8 +575,8 @@ def gather_intel_composer_versions(conf, versions):
batch_file=os.path.join(path,'bin','iclvars.bat')
if os.path.isfile(batch_file):
try:
targets.append((target,(arch,conf.get_msvc_version('intel',version,target,batch_file))))
except conf.errors.ConfigurationError as e:
targets.append((target,(arch,get_compiler_env(conf,'intel',version,target,batch_file))))
except conf.errors.ConfigurationError:
pass
# The intel compilervar_arch.bat is broken when used with Visual Studio Express 2012
# http://software.intel.com/en-us/forums/topic/328487