1 # Copyright (c) 2013 Google Inc. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Handle version information related to Visual Stuio."""
16 class VisualStudioVersion(object):
17 """Information regarding a version of Visual Studio."""
19 def __init__(self, short_name, description,
20 solution_version, project_version, flat_sln, uses_vcxproj,
21 path, sdk_based, default_toolset=None):
22 self.short_name = short_name
23 self.description = description
24 self.solution_version = solution_version
25 self.project_version = project_version
26 self.flat_sln = flat_sln
27 self.uses_vcxproj = uses_vcxproj
29 self.sdk_based = sdk_based
30 self.default_toolset = default_toolset
33 return self.short_name
35 def Description(self):
36 """Get the full description of the version."""
37 return self.description
39 def SolutionVersion(self):
40 """Get the version number of the sln files."""
41 return self.solution_version
43 def ProjectVersion(self):
44 """Get the version number of the vcproj or vcxproj files."""
45 return self.project_version
47 def FlatSolution(self):
50 def UsesVcxproj(self):
51 """Returns true if this version uses a vcxproj file."""
52 return self.uses_vcxproj
54 def ProjectExtension(self):
55 """Returns the file extension for the project."""
56 return self.uses_vcxproj and '.vcxproj' or '.vcproj'
59 """Returns the path to Visual Studio installation."""
62 def ToolPath(self, tool):
63 """Returns the path to a given compiler tool. """
64 return os.path.normpath(os.path.join(self.path, "VC/bin", tool))
66 def DefaultToolset(self):
67 """Returns the msbuild toolset version that will be used in the absence
68 of a user override."""
69 return self.default_toolset
71 def SetupScript(self, target_arch):
72 """Returns a command (with arguments) to be used to set up the
74 # Check if we are running in the SDK command line environment and use
75 # the setup script from the SDK if so. |target_arch| should be either
77 assert target_arch in ('x86', 'x64')
78 sdk_dir = os.environ.get('WindowsSDKDir')
79 if self.sdk_based and sdk_dir:
80 return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
83 # We don't use VC/vcvarsall.bat for x86 because vcvarsall calls
84 # vcvars32, which it can only find if VS??COMNTOOLS is set, which it
86 if target_arch == 'x86':
87 if self.short_name >= '2013' and self.short_name[-1] != 'e' and (
88 os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
89 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
90 # VS2013 and later, non-Express have a x64-x86 cross that we want
92 return [os.path.normpath(
93 os.path.join(self.path, 'VC/vcvarsall.bat')), 'amd64_x86']
94 # Otherwise, the standard x86 compiler.
95 return [os.path.normpath(
96 os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
98 assert target_arch == 'x64'
100 # Use the 64-on-64 compiler if we're not using an express
101 # edition and we're running on a 64bit OS.
102 if self.short_name[-1] != 'e' and (
103 os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
104 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
106 return [os.path.normpath(
107 os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
110 def _RegistryQueryBase(sysdir, key, value):
111 """Use reg.exe to read a particular key.
113 While ideally we might use the win32 module, we would like gyp to be
114 python neutral, so for instance cygwin python lacks this module.
117 sysdir: The system subdirectory to attempt to launch reg.exe from.
118 key: The registry key to read from.
119 value: The particular value to read.
121 stdout from reg.exe, or None for failure.
123 # Skip if not on Windows or Python Win32 setup issue
124 if sys.platform not in ('win32', 'cygwin'):
126 # Setup params to pass to and attempt to launch reg.exe
127 cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
130 cmd.extend(['/v', value])
131 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
132 # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
133 # Note that the error text may be in [1] in some cases
134 text = p.communicate()[0]
135 # Check return code from reg.exe; officially 0==success and 1==error
141 def _RegistryQuery(key, value=None):
142 r"""Use reg.exe to read a particular key through _RegistryQueryBase.
144 First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
145 that fails, it falls back to System32. Sysnative is available on Vista and
146 up and available on Windows Server 2003 and XP through KB patch 942589. Note
147 that Sysnative will always fail if using 64-bit python due to it being a
148 virtual directory and System32 will work correctly in the first place.
150 KB 942589 - http://support.microsoft.com/kb/942589/en-us.
153 key: The registry key.
154 value: The particular registry value to read (optional).
156 stdout from reg.exe, or None for failure.
160 text = _RegistryQueryBase('Sysnative', key, value)
162 if e.errno == errno.ENOENT:
163 text = _RegistryQueryBase('System32', key, value)
169 def _RegistryGetValueUsingWinReg(key, value):
170 """Use the _winreg module to obtain the value of a registry key.
173 key: The registry key.
174 value: The particular registry value to read.
176 contents of the registry key's value, or None on failure. Throws
177 ImportError if _winreg is unavailable.
181 root, subkey = key.split('\\', 1)
182 assert root == 'HKLM' # Only need HKLM for now.
183 with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
184 return _winreg.QueryValueEx(hkey, value)[0]
189 def _RegistryGetValue(key, value):
190 """Use _winreg or reg.exe to obtain the value of a registry key.
192 Using _winreg is preferable because it solves an issue on some corporate
193 environments where access to reg.exe is locked down. However, we still need
194 to fallback to reg.exe for the case where the _winreg module is not available
195 (for example in cygwin python).
198 key: The registry key.
199 value: The particular registry value to read.
201 contents of the registry key's value, or None on failure.
204 return _RegistryGetValueUsingWinReg(key, value)
208 # Fallback to reg.exe if we fail to import _winreg.
209 text = _RegistryQuery(key, value)
213 match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
216 return match.group(1)
219 def _CreateVersion(name, path, sdk_based=False):
220 """Sets up MSVS project generation.
222 Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
223 autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
224 passed in that doesn't match a value in versions python will throw a error.
227 path = os.path.normpath(path)
229 '2015': VisualStudioVersion('2015',
230 'Visual Studio 2015',
231 solution_version='12.00',
232 project_version='14.0',
237 default_toolset='v140'),
238 '2013': VisualStudioVersion('2013',
239 'Visual Studio 2013',
240 solution_version='13.00',
241 project_version='12.0',
246 default_toolset='v120'),
247 '2013e': VisualStudioVersion('2013e',
248 'Visual Studio 2013',
249 solution_version='13.00',
250 project_version='12.0',
255 default_toolset='v120'),
256 '2012': VisualStudioVersion('2012',
257 'Visual Studio 2012',
258 solution_version='12.00',
259 project_version='4.0',
264 default_toolset='v110'),
265 '2012e': VisualStudioVersion('2012e',
266 'Visual Studio 2012',
267 solution_version='12.00',
268 project_version='4.0',
273 default_toolset='v110'),
274 '2010': VisualStudioVersion('2010',
275 'Visual Studio 2010',
276 solution_version='11.00',
277 project_version='4.0',
281 sdk_based=sdk_based),
282 '2010e': VisualStudioVersion('2010e',
283 'Visual C++ Express 2010',
284 solution_version='11.00',
285 project_version='4.0',
289 sdk_based=sdk_based),
290 '2008': VisualStudioVersion('2008',
291 'Visual Studio 2008',
292 solution_version='10.00',
293 project_version='9.00',
297 sdk_based=sdk_based),
298 '2008e': VisualStudioVersion('2008e',
299 'Visual Studio 2008',
300 solution_version='10.00',
301 project_version='9.00',
305 sdk_based=sdk_based),
306 '2005': VisualStudioVersion('2005',
307 'Visual Studio 2005',
308 solution_version='9.00',
309 project_version='8.00',
313 sdk_based=sdk_based),
314 '2005e': VisualStudioVersion('2005e',
315 'Visual Studio 2005',
316 solution_version='9.00',
317 project_version='8.00',
321 sdk_based=sdk_based),
323 return versions[str(name)]
326 def _ConvertToCygpath(path):
327 """Convert to cygwin path if we are using cygwin."""
328 if sys.platform == 'cygwin':
329 p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
330 path = p.communicate()[0].strip()
334 def _DetectVisualStudioVersions(versions_to_check, force_express):
335 """Collect the list of installed visual studio versions.
338 A list of visual studio versions installed in descending order of
340 Base this on the registry and a quick check if devenv.exe exists.
341 Only versions 8-10 are considered.
343 2005(e) - Visual Studio 2005 (8)
344 2008(e) - Visual Studio 2008 (9)
345 2010(e) - Visual Studio 2010 (10)
346 2012(e) - Visual Studio 2012 (11)
347 2013(e) - Visual Studio 2013 (12)
348 2015 - Visual Studio 2015 (14)
349 Where (e) is e for express editions of MSVS and blank otherwise.
360 for version in versions_to_check:
361 # Old method of searching for which VS version is installed
362 # We don't use the 2010-encouraged-way because we also want to get the
363 # path to the binaries, which it doesn't offer.
364 keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
365 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
366 r'HKLM\Software\Microsoft\VCExpress\%s' % version,
367 r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
368 for index in range(len(keys)):
369 path = _RegistryGetValue(keys[index], 'InstallDir')
372 path = _ConvertToCygpath(path)
374 full_path = os.path.join(path, 'devenv.exe')
375 express_path = os.path.join(path, '*express.exe')
376 if not force_express and os.path.exists(full_path):
378 versions.append(_CreateVersion(version_to_year[version],
379 os.path.join(path, '..', '..')))
381 elif glob.glob(express_path):
383 versions.append(_CreateVersion(version_to_year[version] + 'e',
384 os.path.join(path, '..', '..')))
386 # The old method above does not work when only SDK is installed.
387 keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
388 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
389 for index in range(len(keys)):
390 path = _RegistryGetValue(keys[index], version)
393 path = _ConvertToCygpath(path)
394 if version != '14.0': # There is no Express edition for 2015.
395 versions.append(_CreateVersion(version_to_year[version] + 'e',
396 os.path.join(path, '..'), sdk_based=True))
401 def SelectVisualStudioVersion(version='auto', allow_fallback=True):
402 """Select which version of Visual Studio projects to generate.
405 version: Hook to allow caller to force a particular version (vs auto).
407 An object representing a visual studio project format version.
409 # In auto mode, check environment variable for override.
410 if version == 'auto':
411 version = os.environ.get('GYP_MSVS_VERSION', 'auto')
413 'auto': ('14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
426 override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
428 msvs_version = os.environ.get('GYP_MSVS_VERSION')
430 raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
431 'set to a particular version (e.g. 2010e).')
432 return _CreateVersion(msvs_version, override_path, sdk_based=True)
433 version = str(version)
434 versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
436 if not allow_fallback:
437 raise ValueError('Could not locate Visual Studio installation.')
438 if version == 'auto':
439 # Default to 2005 if we couldn't find anything
440 return _CreateVersion('2005', None)
442 return _CreateVersion(version, None)