3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 MS Compilers: detect Visual Studio and/or Visual C/C++
44 get_msvc_version_numeric
,
50 # Visual Studio express version policy when unqualified version is not installed:
51 # True: use express version for unqualified version (e.g., use 12.0Exp for 12.0)
52 # False: do not use express version for unqualified version
53 _VSEXPRESS_USE_VERSTR
= True
58 An abstract base class for trying to find installed versions of
61 def __init__(self
, version
, **kw
) -> None:
62 self
.version
= version
63 self
.verstr
= get_msvc_version_numeric(version
)
64 self
.vernum
= float(self
.verstr
)
65 self
.is_express
= True if self
.verstr
!= self
.version
else False
66 kw
['vc_version'] = kw
.get('vc_version', version
)
67 kw
['sdk_version'] = kw
.get('sdk_version', version
)
68 self
.__dict
__.update(kw
)
71 def find_batch_file(self
):
72 vs_dir
= self
.get_vs_dir()
76 batch_file
= os
.path
.join(vs_dir
, self
.batch_file_path
)
77 batch_file
= os
.path
.normpath(batch_file
)
78 if not os
.path
.isfile(batch_file
):
79 debug('%s not on file system', batch_file
)
83 def find_vs_dir_by_vc(self
, env
):
84 dir = find_vc_pdir(self
.vc_version
, env
)
86 debug('no installed VC %s', self
.vc_version
)
88 return os
.path
.abspath(os
.path
.join(dir, os
.pardir
))
90 def find_vs_dir_by_reg(self
, env
):
94 root
= root
+ 'Wow6432Node\\'
95 for key
in self
.hkeys
:
97 return self
.find_vs_dir_by_vc(env
)
100 comps
= read_reg(key
)
102 debug('no VS registry key %s', repr(key
))
104 debug('found VS in registry: %s', comps
)
108 def find_vs_dir(self
, env
):
109 """ Can use registry or location of VC to find vs dir
110 First try to find by registry, and if that fails find via VC dir
113 vs_dir
= self
.find_vs_dir_by_reg(env
)
115 vs_dir
= self
.find_vs_dir_by_vc(env
)
116 debug('found VS in %s', str(vs_dir
))
119 def find_executable(self
, env
):
120 vs_dir
= self
.get_vs_dir(env
)
122 debug('no vs_dir (%s)', vs_dir
)
124 executable
= os
.path
.join(vs_dir
, self
.executable_path
)
125 executable
= os
.path
.normpath(executable
)
126 if not os
.path
.isfile(executable
):
127 debug('%s not on file system', executable
)
131 def get_batch_file(self
):
133 return self
._cache
['batch_file']
135 batch_file
= self
.find_batch_file()
136 self
._cache
['batch_file'] = batch_file
139 def get_executable(self
, env
=None):
141 debug('using cache:%s', self
._cache
['executable'])
142 return self
._cache
['executable']
144 executable
= self
.find_executable(env
)
145 self
._cache
['executable'] = executable
146 debug('not in cache:%s', executable
)
149 def get_vs_dir(self
, env
=None):
151 return self
._cache
['vs_dir']
153 vs_dir
= self
.find_vs_dir(env
)
154 self
._cache
['vs_dir'] = vs_dir
157 def get_supported_arch(self
):
159 return self
._cache
['supported_arch']
161 # RDEVE: for the time being use hardcoded lists
162 # supported_arch = self.find_supported_arch()
163 self
._cache
['supported_arch'] = self
.supported_arch
164 return self
.supported_arch
166 def reset(self
) -> None:
169 # The list of supported Visual Studio versions we know how to detect.
171 # How to look for .bat file ?
172 # - VS 2008 Express (x86):
173 # * from registry key productdir, gives the full path to vsvarsall.bat. In
174 # HKEY_LOCAL_MACHINE):
175 # Software\Microsoft\VCEpress\9.0\Setup\VC\productdir
176 # * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC
177 # relatively to the path given by the variable.
179 # - VS 2008 Express (WoW6432: 32 bits on windows x64):
180 # Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir
182 # - VS 2005 Express (x86):
183 # * from registry key productdir, gives the full path to vsvarsall.bat. In
184 # HKEY_LOCAL_MACHINE):
185 # Software\Microsoft\VCEpress\8.0\Setup\VC\productdir
186 # * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC
187 # relatively to the path given by the variable.
189 # - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a
192 # - VS 2003 .Net (pro edition ? x86):
193 # * from registry key productdir. The path is then ..\Common7\Tools\
194 # relatively to the key. The key is in HKEY_LOCAL_MACHINE):
195 # Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir
196 # * from environmnent variable VS71COMNTOOLS: the path is the full path to
200 # * from registry key productdir. The path is then Bin
201 # relatively to the key. The key is in HKEY_LOCAL_MACHINE):
202 # Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir
204 # The first version found in the list is the one used by default if
205 # there are multiple versions installed. Barring good reasons to
206 # the contrary, this means we should list versions from most recent
207 # to oldest. Pro versions get listed before Express versions on the
208 # assumption that, by default, you'd rather use the version you paid
209 # good money for in preference to whatever Microsoft makes available
212 # If you update this list, update _VCVER and _VCVER_TO_PRODUCT_DIR in
213 # Tool/MSCommon/vc.py, and the MSVC_VERSION documentation in Tool/msvc.xml.
221 common_tools_var
='VS170COMNTOOLS',
222 executable_path
=r
'Common7\IDE\devenv.com',
223 # should be a fallback, prefer use vswhere installationPath
224 batch_file_path
=r
'Common7\Tools\VsDevCmd.bat',
225 supported_arch
=['x86', 'amd64', "arm", 'arm64'],
233 common_tools_var
='VS160COMNTOOLS',
234 executable_path
=r
'Common7\IDE\devenv.com',
235 # should be a fallback, prefer use vswhere installationPath
236 batch_file_path
=r
'Common7\Tools\VsDevCmd.bat',
237 supported_arch
=['x86', 'amd64', "arm", 'arm64'],
245 common_tools_var
='VS150COMNTOOLS',
246 executable_path
=r
'Common7\IDE\devenv.com',
247 # should be a fallback, prefer use vswhere installationPath
248 batch_file_path
=r
'Common7\Tools\VsDevCmd.bat',
249 supported_arch
=['x86', 'amd64', "arm", 'arm64'],
252 # Visual C++ 2017 Express Edition (for Desktop)
253 VisualStudio('14.1Exp',
257 common_tools_var
='VS150COMNTOOLS',
258 executable_path
=r
'Common7\IDE\WDExpress.exe',
259 # should be a fallback, prefer use vswhere installationPath
260 batch_file_path
=r
'Common7\Tools\VsDevCmd.bat',
261 supported_arch
=['x86', 'amd64', "arm", 'arm64'],
268 hkeys
=[r
'Microsoft\VisualStudio\14.0\Setup\VS\ProductDir'],
269 common_tools_var
='VS140COMNTOOLS',
270 executable_path
=r
'Common7\IDE\devenv.com',
271 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
272 supported_arch
=['x86', 'amd64', "arm"],
275 # Visual C++ 2015 Express Edition (for Desktop)
276 VisualStudio('14.0Exp',
279 hkeys
=[r
'Microsoft\VisualStudio\14.0\Setup\VS\ProductDir'],
280 common_tools_var
='VS140COMNTOOLS',
281 executable_path
=r
'Common7\IDE\WDExpress.exe',
282 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
283 supported_arch
=['x86', 'amd64', "arm"],
290 hkeys
=[r
'Microsoft\VisualStudio\12.0\Setup\VS\ProductDir'],
291 common_tools_var
='VS120COMNTOOLS',
292 executable_path
=r
'Common7\IDE\devenv.com',
293 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
294 supported_arch
=['x86', 'amd64'],
297 # Visual C++ 2013 Express Edition (for Desktop)
298 VisualStudio('12.0Exp',
301 hkeys
=[r
'Microsoft\VisualStudio\12.0\Setup\VS\ProductDir'],
302 common_tools_var
='VS120COMNTOOLS',
303 executable_path
=r
'Common7\IDE\WDExpress.exe',
304 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
305 supported_arch
=['x86', 'amd64'],
311 hkeys
=[r
'Microsoft\VisualStudio\11.0\Setup\VS\ProductDir'],
312 common_tools_var
='VS110COMNTOOLS',
313 executable_path
=r
'Common7\IDE\devenv.com',
314 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
315 supported_arch
=['x86', 'amd64'],
318 # Visual C++ 2012 Express Edition (for Desktop)
319 VisualStudio('11.0Exp',
322 hkeys
=[r
'Microsoft\VisualStudio\11.0\Setup\VS\ProductDir'],
323 common_tools_var
='VS110COMNTOOLS',
324 executable_path
=r
'Common7\IDE\WDExpress.exe',
325 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
326 supported_arch
=['x86', 'amd64'],
332 hkeys
=[r
'Microsoft\VisualStudio\10.0\Setup\VS\ProductDir'],
333 common_tools_var
='VS100COMNTOOLS',
334 executable_path
=r
'Common7\IDE\devenv.com',
335 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
336 supported_arch
=['x86', 'amd64'],
339 # Visual C++ 2010 Express Edition
340 VisualStudio('10.0Exp',
343 hkeys
=[r
'Microsoft\VCExpress\10.0\Setup\VS\ProductDir'],
344 common_tools_var
='VS100COMNTOOLS',
345 executable_path
=r
'Common7\IDE\VCExpress.exe',
346 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
347 supported_arch
=['x86'],
353 hkeys
=[r
'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'],
354 common_tools_var
='VS90COMNTOOLS',
355 executable_path
=r
'Common7\IDE\devenv.com',
356 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
357 supported_arch
=['x86', 'amd64'],
360 # Visual C++ 2008 Express Edition
361 VisualStudio('9.0Exp',
364 hkeys
=[r
'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'],
365 common_tools_var
='VS90COMNTOOLS',
366 executable_path
=r
'Common7\IDE\VCExpress.exe',
367 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
368 supported_arch
=['x86'],
374 hkeys
=[r
'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'],
375 common_tools_var
='VS80COMNTOOLS',
376 executable_path
=r
'Common7\IDE\devenv.com',
377 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
378 default_dirname
='Microsoft Visual Studio 8',
379 supported_arch
=['x86', 'amd64'],
382 # Visual C++ 2005 Express Edition
383 VisualStudio('8.0Exp',
386 hkeys
=[r
'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'],
387 common_tools_var
='VS80COMNTOOLS',
388 executable_path
=r
'Common7\IDE\VCExpress.exe',
389 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
390 default_dirname
='Microsoft Visual Studio 8',
391 supported_arch
=['x86'],
394 # Visual Studio .NET 2003
397 hkeys
=[r
'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'],
398 common_tools_var
='VS71COMNTOOLS',
399 executable_path
=r
'Common7\IDE\devenv.com',
400 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
401 default_dirname
='Microsoft Visual Studio .NET 2003',
402 supported_arch
=['x86'],
407 sdk_version
='2003R2',
408 hkeys
=[r
'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'],
409 common_tools_var
='VSCOMNTOOLS',
410 executable_path
=r
'Common7\IDE\devenv.com',
411 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
412 default_dirname
='Microsoft Visual Studio .NET',
413 supported_arch
=['x86'],
418 sdk_version
='2003R1',
419 hkeys
=[r
'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir',
421 common_tools_var
='MSDevDir',
422 executable_path
=r
'Common\MSDev98\Bin\MSDEV.COM',
423 batch_file_path
=r
'Common7\Tools\vsvars32.bat',
424 default_dirname
='Microsoft Visual Studio',
425 supported_arch
=['x86'],
430 for vs
in SupportedVSList
:
431 SupportedVSMap
[vs
.version
] = vs
434 # Finding installed versions of Visual Studio isn't cheap, because it
435 # goes not only to the registry but also to the disk to sanity-check
436 # that there is, in fact, a Visual Studio directory there and that the
437 # registry entry isn't just stale. Find this information once, when
438 # requested, and cache it.
440 InstalledVSList
= None
441 InstalledVSMap
= None
443 def get_installed_visual_studios(env
=None):
444 global InstalledVSList
445 global InstalledVSMap
446 vswhere_freeze_env(env
)
447 if InstalledVSList
is None:
450 for vs
in SupportedVSList
:
451 debug('trying to find VS %s', vs
.version
)
452 if vs
.get_executable(env
):
453 debug('found VS %s', vs
.version
)
454 InstalledVSList
.append(vs
)
455 if vs
.is_express
and vs
.verstr
not in InstalledVSMap
:
456 if _VSEXPRESS_USE_VERSTR
:
457 InstalledVSMap
[vs
.verstr
] = vs
458 InstalledVSMap
[vs
.version
] = vs
459 return InstalledVSList
461 def _get_installed_vss(env
=None):
462 get_installed_visual_studios(env
)
463 versions
= list(InstalledVSMap
.keys())
466 def reset_installed_visual_studios() -> None:
467 global InstalledVSList
468 global InstalledVSMap
470 InstalledVSList
= None
471 InstalledVSMap
= None
472 for vs
in SupportedVSList
:
475 # Need to clear installed VC's as well as they are used in finding
477 reset_installed_vcs()
480 # We may be asked to update multiple construction environments with
481 # SDK information. When doing this, we check on-disk for whether
482 # the SDK has 'mfc' and 'atl' subdirectories. Since going to disk
483 # is expensive, cache results by directory.
485 #SDKEnvironmentUpdates = {}
487 #def set_sdk_by_directory(env, sdk_dir):
488 # global SDKEnvironmentUpdates
490 # env_tuple_list = SDKEnvironmentUpdates[sdk_dir]
492 # env_tuple_list = []
493 # SDKEnvironmentUpdates[sdk_dir] = env_tuple_list
495 # include_path = os.path.join(sdk_dir, 'include')
496 # mfc_path = os.path.join(include_path, 'mfc')
497 # atl_path = os.path.join(include_path, 'atl')
499 # if os.path.exists(mfc_path):
500 # env_tuple_list.append(('INCLUDE', mfc_path))
501 # if os.path.exists(atl_path):
502 # env_tuple_list.append(('INCLUDE', atl_path))
503 # env_tuple_list.append(('INCLUDE', include_path))
505 # env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib')))
506 # env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib')))
507 # env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin')))
509 # for variable, directory in env_tuple_list:
510 # env.PrependENVPath(variable, directory)
512 def msvs_exists(env
=None) -> bool:
513 return len(get_installed_visual_studios(env
)) > 0
515 def get_vs_by_version(msvs
, env
=None):
516 global InstalledVSMap
517 global SupportedVSMap
520 if msvs
not in SupportedVSMap
:
521 msg
= "Visual Studio version %s is not supported" % repr(msvs
)
522 raise SCons
.Errors
.UserError(msg
)
523 get_installed_visual_studios(env
)
524 vs
= InstalledVSMap
.get(msvs
)
525 debug('InstalledVSMap:%s', InstalledVSMap
)
526 debug('found vs:%s', vs
)
527 # Some check like this would let us provide a useful error message
528 # if they try to set a Visual Studio version that's not installed.
529 # However, we also want to be able to run tests (like the unit
530 # tests) on systems that don't, or won't ever, have it installed.
531 # It might be worth resurrecting this, with some configurable
532 # setting that the tests can use to bypass the check.
534 # msg = "Visual Studio version %s is not installed" % repr(msvs)
535 # raise SCons.Errors.UserError, msg
538 def get_default_version(env
):
539 """Returns the default version string to use for MSVS.
541 If no version was requested by the user through the MSVS environment
542 variable, query all the available visual studios through
543 get_installed_visual_studios, and take the highest one.
550 if 'MSVS' not in env
or not SCons
.Util
.is_Dict(env
['MSVS']):
551 # get all versions, and remember them for speed later
552 versions
= _get_installed_vss(env
)
553 env
['MSVS'] = {'VERSIONS' : versions
}
555 versions
= env
['MSVS'].get('VERSIONS', [])
557 if 'MSVS_VERSION' not in env
:
559 env
['MSVS_VERSION'] = versions
[0] #use highest version by default
561 debug('WARNING: no installed versions found, '
562 'using first in SupportedVSList (%s)',
563 SupportedVSList
[0].version
)
564 env
['MSVS_VERSION'] = SupportedVSList
[0].version
566 env
['MSVS']['VERSION'] = env
['MSVS_VERSION']
568 return env
['MSVS_VERSION']
570 def get_default_arch(env
):
571 """Return the default arch to use for MSVS
573 if no version was requested by the user through the MSVS_ARCH environment
580 arch
= env
.get('MSVS_ARCH', 'x86')
582 msvs
= InstalledVSMap
.get(env
['MSVS_VERSION'])
586 elif arch
not in msvs
.get_supported_arch():
587 fmt
= "Visual Studio version %s does not support architecture %s"
588 raise SCons
.Errors
.UserError(fmt
% (env
['MSVS_VERSION'], arch
))
592 def merge_default_version(env
) -> None:
593 version
= get_default_version(env
)
594 arch
= get_default_arch(env
)
596 # TODO: refers to versions and arch which aren't defined; called nowhere. Drop?
597 def msvs_setup_env(env
) -> None:
598 msvs
= get_vs_by_version(version
, env
)
601 batfilename
= msvs
.get_batch_file()
603 # XXX: I think this is broken. This will silently set a bogus tool instead
604 # of failing, but there is no other way with the current scons tool
606 if batfilename
is not None:
608 vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE')
610 msvs_list
= get_installed_visual_studios(env
)
611 vscommonvarnames
= [vs
.common_tools_var
for vs
in msvs_list
]
612 save_ENV
= env
['ENV']
613 nenv
= normalize_env(env
['ENV'],
614 ['COMSPEC'] + vscommonvarnames
,
617 output
= get_output(batfilename
, arch
, env
=nenv
)
619 env
['ENV'] = save_ENV
620 vars = parse_output(output
, vars)
622 for k
, v
in vars.items():
623 env
.PrependENVPath(k
, v
, delete_existing
=1)
625 def query_versions(env
=None):
626 """Query the system to get available versions of VS. A version is
627 considered when a batfile is found."""
628 versions
= _get_installed_vss(env
)
633 # indent-tabs-mode:nil
635 # vim: set expandtab tabstop=4 shiftwidth=4: