4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 # * supported arch for versions: for old versions of batch file without
26 # argument, giving bogus argument cannot be detected, so we have to hardcode
28 # * print warning when msvc version specified but not found
29 # * find out why warning do not print
30 # * test on 64 bits XP + VS 2005 (and VS 6 if possible)
33 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
35 __doc__ = """Module for Visual C/C++ detection and configuration.
41 from string import digits as string_digits
51 get_installed_sdks = sdk.get_installed_sdks
54 class VisualCException(Exception):
57 class UnsupportedVersion(VisualCException):
60 class UnsupportedArch(VisualCException):
63 class MissingConfiguration(VisualCException):
66 class NoVersionFound(VisualCException):
69 class BatchFileExecutionError(VisualCException):
72 # Dict to 'canonalize' the arch
73 _ARCH_TO_CANONICAL = {
83 # Given a (host, target) tuple, return the argument for the bat file. Both host
84 # and targets should be canonalized.
85 _HOST_TARGET_ARCH_TO_BAT_ARCH = {
86 ("x86", "x86"): "x86",
87 ("x86", "amd64"): "x86_amd64",
88 ("amd64", "amd64"): "amd64",
89 ("amd64", "x86"): "x86",
90 ("x86", "ia64"): "x86_ia64"
93 def get_host_target(env):
94 host_platform = env.get('HOST_ARCH')
96 host_platform = platform.machine()
97 # TODO(2.5): the native Python platform.machine() function returns
98 # '' on all Python versions before 2.6, after which it also uses
99 # PROCESSOR_ARCHITECTURE.
100 if not host_platform:
101 host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '')
102 target_platform = env.get('TARGET_ARCH')
103 if not target_platform:
104 target_platform = host_platform
107 host = _ARCH_TO_CANONICAL[host_platform]
109 msg = "Unrecognized host architecture %s"
110 raise ValueError(msg % repr(host_platform))
113 target = _ARCH_TO_CANONICAL[target_platform]
115 raise ValueError("Unrecognized target architecture %s" % target_platform)
117 return (host, target)
119 _VCVER = ["10.0", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"]
121 _VCVER_TO_PRODUCT_DIR = {
123 r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'],
125 r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir'],
127 r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'],
129 r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'],
131 r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'],
133 r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'],
135 r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'],
137 r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir']
140 def msvc_version_to_maj_min(msvc_version):
141 msvc_version_numeric = string.join(filter(lambda x: x in string.digits + ".", msvc_version), '')
143 t = msvc_version_numeric.split(".")
145 raise ValueError("Unrecognized version %s" % msvc_version)
150 except ValueError, e:
151 raise ValueError("Unrecognized version %s" % msvc_version)
153 def is_host_target_supported(host_target, msvc_version):
154 """Return True if the given (host, target) tuple is supported given the
160 tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross
161 compilation from 32 bits windows to 64 bits.
163 msvc version (major.minor, e.g. 10.0)
167 This only check whether a given version *may* support the given (host,
168 target), not that the toolchain is actually present on the machine.
170 # We assume that any Visual Studio version supports x86 as a target
171 if host_target[1] != "x86":
172 maj, min = msvc_version_to_maj_min(msvc_version)
178 def find_vc_pdir(msvc_version):
179 """Try to find the product directory for the given
184 If for some reason the requested version could not be found, an
185 exception which inherits from VisualCException will be raised."""
187 if common.is_win64():
188 root = root + 'Wow6432Node\\'
190 hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version]
192 debug("Unknown version of MSVC: %s" % msvc_version)
193 raise UnsupportedVersion("Unknown version %s" % msvc_version)
198 comps = common.read_reg(key)
199 except WindowsError, e:
200 debug('find_vc_dir(): no VC registry key %s' % repr(key))
202 debug('find_vc_dir(): found VC in registry: %s' % comps)
203 if os.path.exists(comps):
206 debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\
208 raise MissingConfiguration("registry dir %s not found on the filesystem" % comps)
211 def find_batch_file(env,msvc_version):
213 Find the location of the batch script which should set up the compiler
214 for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress
216 pdir = find_vc_pdir(msvc_version)
218 raise NoVersionFound("No version of Visual Studio found")
220 debug('vc.py: find_batch_file() pdir:%s'%pdir)
222 # filter out e.g. "Exp" from the version name
223 msvc_ver_numeric = ''.join([x for x in msvc_version if x in string_digits + "."])
224 vernum = float(msvc_ver_numeric)
226 pdir = os.path.join(pdir, os.pardir, "Common7", "Tools")
227 batfilename = os.path.join(pdir, "vsvars32.bat")
229 pdir = os.path.join(pdir, "Bin")
230 batfilename = os.path.join(pdir, "vcvars32.bat")
232 batfilename = os.path.join(pdir, "vcvarsall.bat")
234 if not os.path.exists(batfilename):
235 debug("Not found: %s" % batfilename)
238 installed_sdks=get_installed_sdks()
239 (host_arch,target_arch)=get_host_target(env)
240 for _sdk in installed_sdks:
241 sdk_bat_file=_sdk.get_sdk_vc_script(host_arch,target_arch)
242 sdk_bat_file_path=os.path.join(pdir,sdk_bat_file)
243 debug('vc.py:find_batch_file() sdk_bat_file_path:%s'%sdk_bat_file_path)
244 if os.path.exists(sdk_bat_file_path):
245 return (batfilename,sdk_bat_file_path)
247 debug("vc.py:find_batch_file() not found:%s"%sdk_bat_file_path)
249 return (batfilename,None)
251 __INSTALLED_VCS_RUN = None
253 def cached_get_installed_vcs():
254 global __INSTALLED_VCS_RUN
256 if __INSTALLED_VCS_RUN is None:
257 ret = get_installed_vcs()
258 __INSTALLED_VCS_RUN = ret
260 return __INSTALLED_VCS_RUN
262 def get_installed_vcs():
263 installed_versions = []
265 debug('trying to find VC %s' % ver)
267 if find_vc_pdir(ver):
268 debug('found VC %s' % ver)
269 installed_versions.append(ver)
271 debug('find_vc_pdir return None for ver %s' % ver)
272 except VisualCException, e:
273 debug('did not find VC %s: caught exception %s' % (ver, str(e)))
274 return installed_versions
276 def reset_installed_vcs():
277 """Make it try again to find VC. This is just for the tests."""
278 __INSTALLED_VCS_RUN = None
280 def script_env(script, args=None):
281 stdout = common.get_output(script, args)
282 # Stupid batch files do not set return code: we take a look at the
283 # beginning of the output for an error message instead
284 olines = stdout.splitlines()
285 if olines[0].startswith("The specified configuration type is missing"):
286 raise BatchFileExecutionError("\n".join(olines[:2]))
288 return common.parse_output(stdout)
290 def get_default_version(env):
291 debug('get_default_version()')
293 msvc_version = env.get('MSVC_VERSION')
294 msvs_version = env.get('MSVS_VERSION')
296 debug('get_default_version(): msvc_version:%s msvs_version:%s'%(msvc_version,msvs_version))
298 if msvs_version and not msvc_version:
300 SCons.Warnings.DeprecatedWarning,
301 "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ")
303 elif msvc_version and msvs_version:
304 if not msvc_version == msvs_version:
306 SCons.Warnings.VisualVersionMismatch,
307 "Requested msvc version (%s) and msvs version (%s) do " \
308 "not match: please use MSVC_VERSION only to request a " \
309 "visual studio version, MSVS_VERSION is deprecated" \
310 % (msvc_version, msvs_version))
313 installed_vcs = cached_get_installed_vcs()
314 debug('installed_vcs:%s' % installed_vcs)
315 if not installed_vcs:
316 msg = 'No installed VCs'
317 debug('msv %s\n' % repr(msg))
318 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
320 msvc_version = installed_vcs[0]
321 debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version))
325 def msvc_setup_env_once(env):
327 has_run = env["MSVC_SETUP_RUN"]
333 env["MSVC_SETUP_RUN"] = True
335 def msvc_setup_env(env):
336 debug('msvc_setup_env()')
338 version = get_default_version(env)
340 warn_msg = "No version of Visual Studio compiler found - C/C++ " \
341 "compilers most likely not set correctly"
342 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
344 debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version))
346 # XXX: we set-up both MSVS version for backward
347 # compatibility with the msvs tool
348 env['MSVC_VERSION'] = version
349 env['MSVS_VERSION'] = version
353 (vc_script,sdk_script) = find_batch_file(env,version)
354 debug('vc.py:msvc_setup_env() vc_script:%s sdk_script:%s'%(vc_script,sdk_script))
355 except VisualCException, e:
357 debug('Caught exception while looking for batch file (%s)' % msg)
358 warn_msg = "VC version %s not installed. " + \
359 "C/C++ compilers are most likely not set correctly.\n" + \
360 " Installed versions are: %s"
361 warn_msg = warn_msg % (version, cached_get_installed_vcs())
362 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
365 debug('vc.py:msvc_setup_env() vc_script:%s sdk_script:%s'%(vc_script,sdk_script))
366 use_script = env.get('MSVC_USE_SCRIPT', True)
367 if SCons.Util.is_String(use_script):
368 debug('use_script 1 %s\n' % repr(use_script))
369 d = script_env(use_script)
371 host_platform, target_platform = get_host_target(env)
372 host_target = (host_platform, target_platform)
373 if not is_host_target_supported(host_target, version):
374 warn_msg = "host, target = %s not supported for MSVC version %s" % \
375 (host_target, version)
376 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
377 arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target]
378 debug('use_script 2 %s, args:%s\n' % (repr(vc_script), arg))
381 d = script_env(vc_script, args=arg)
382 except BatchFileExecutionError, e:
383 debug('use_script 3: failed running VC script %s: %s: Error:%s'%(repr(vc_script),arg,e))
385 if not vc_script and sdk_script:
386 debug('use_script 4: trying sdk script: %s'%(sdk_script))
388 d = script_env(sdk_script,args=[])
389 except BatchFileExecutionError,e:
390 debug('use_script 5: failed running SDK script %s: Error:%s'%(repr(sdk_script),e))
392 elif not vc_script and not sdk_script:
393 debug('use_script 6: Neither VC script nor SDK script found')
397 debug('MSVC_USE_SCRIPT set to False')
398 warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \
400 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
403 for k, v in d.items():
404 debug('vc.py:msvc_setup_env() env:%s -> %s'%(k,v))
405 env.PrependENVPath(k, v, delete_existing=True)
407 def msvc_exists(version=None):
408 vcs = cached_get_installed_vcs()
411 return version in vcs