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."""
15 class VisualStudioVersion(object):
16 """Information regarding a version of Visual Studio."""
18 def __init__(self, short_name, description,
19 solution_version, project_version, flat_sln, uses_vcxproj,
20 path, sdk_based, default_toolset=None):
21 self.short_name = short_name
22 self.description = description
23 self.solution_version = solution_version
24 self.project_version = project_version
25 self.flat_sln = flat_sln
26 self.uses_vcxproj = uses_vcxproj
28 self.sdk_based = sdk_based
29 self.default_toolset = default_toolset
32 return self.short_name
34 def Description(self):
35 """Get the full description of the version."""
36 return self.description
38 def SolutionVersion(self):
39 """Get the version number of the sln files."""
40 return self.solution_version
42 def ProjectVersion(self):
43 """Get the version number of the vcproj or vcxproj files."""
44 return self.project_version
46 def FlatSolution(self):
49 def UsesVcxproj(self):
50 """Returns true if this version uses a vcxproj file."""
51 return self.uses_vcxproj
53 def ProjectExtension(self):
54 """Returns the file extension for the project."""
55 return self.uses_vcxproj and '.vcxproj' or '.vcproj'
58 """Returns the path to Visual Studio installation."""
61 def ToolPath(self, tool):
62 """Returns the path to a given compiler tool. """
63 return os.path.normpath(os.path.join(self.path, "VC/bin", tool))
65 def DefaultToolset(self):
66 """Returns the msbuild toolset version that will be used in the absence
67 of a user override."""
68 return self.default_toolset
70 def SetupScript(self, target_arch):
71 """Returns a command (with arguments) to be used to set up the
73 # Check if we are running in the SDK command line environment and use
74 # the setup script from the SDK if so. |target_arch| should be either
76 assert target_arch in ('x86', 'x64')
77 sdk_dir = os.environ.get('WindowsSDKDir')
78 if self.sdk_based and sdk_dir:
79 return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')),
82 # We don't use VC/vcvarsall.bat for x86 because vcvarsall calls
83 # vcvars32, which it can only find if VS??COMNTOOLS is set, which it
85 if target_arch == 'x86':
86 if self.short_name == '2013' and (
87 os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
88 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
89 # VS2013 non-Express has a x64-x86 cross that we want to prefer.
90 return [os.path.normpath(
91 os.path.join(self.path, 'VC/vcvarsall.bat')), 'amd64_x86']
92 # Otherwise, the standard x86 compiler.
93 return [os.path.normpath(
94 os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
96 assert target_arch == 'x64'
98 if (os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
99 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
100 # Use the 64-on-64 compiler if we can.
102 return [os.path.normpath(
103 os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
106 def _RegistryQueryBase(sysdir, key, value):
107 """Use reg.exe to read a particular key.
109 While ideally we might use the win32 module, we would like gyp to be
110 python neutral, so for instance cygwin python lacks this module.
113 sysdir: The system subdirectory to attempt to launch reg.exe from.
114 key: The registry key to read from.
115 value: The particular value to read.
117 stdout from reg.exe, or None for failure.
119 # Skip if not on Windows or Python Win32 setup issue
120 if sys.platform not in ('win32', 'cygwin'):
122 # Setup params to pass to and attempt to launch reg.exe
123 cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
126 cmd.extend(['/v', value])
127 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
128 # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
129 # Note that the error text may be in [1] in some cases
130 text = p.communicate()[0]
131 # Check return code from reg.exe; officially 0==success and 1==error
137 def _RegistryQuery(key, value=None):
138 """Use reg.exe to read a particular key through _RegistryQueryBase.
140 First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
141 that fails, it falls back to System32. Sysnative is available on Vista and
142 up and available on Windows Server 2003 and XP through KB patch 942589. Note
143 that Sysnative will always fail if using 64-bit python due to it being a
144 virtual directory and System32 will work correctly in the first place.
146 KB 942589 - http://support.microsoft.com/kb/942589/en-us.
149 key: The registry key.
150 value: The particular registry value to read (optional).
152 stdout from reg.exe, or None for failure.
156 text = _RegistryQueryBase('Sysnative', key, value)
158 if e.errno == errno.ENOENT:
159 text = _RegistryQueryBase('System32', key, value)
165 def _RegistryGetValue(key, value):
166 """Use reg.exe to obtain the value of a registry key.
169 key: The registry key.
170 value: The particular registry value to read.
172 contents of the registry key's value, or None on failure.
174 text = _RegistryQuery(key, value)
178 match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
181 return match.group(1)
184 def _RegistryKeyExists(key):
185 """Use reg.exe to see if a key exists.
188 key: The registry key to check.
190 True if the key exists
192 if not _RegistryQuery(key):
197 def _CreateVersion(name, path, sdk_based=False):
198 """Sets up MSVS project generation.
200 Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
201 autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
202 passed in that doesn't match a value in versions python will throw a error.
205 path = os.path.normpath(path)
207 '2013': VisualStudioVersion('2013',
208 'Visual Studio 2013',
209 solution_version='13.00',
210 project_version='12.0',
215 default_toolset='v120'),
216 '2013e': VisualStudioVersion('2013e',
217 'Visual Studio 2013',
218 solution_version='13.00',
219 project_version='12.0',
224 default_toolset='v120'),
225 '2012': VisualStudioVersion('2012',
226 'Visual Studio 2012',
227 solution_version='12.00',
228 project_version='4.0',
233 default_toolset='v110'),
234 '2012e': VisualStudioVersion('2012e',
235 'Visual Studio 2012',
236 solution_version='12.00',
237 project_version='4.0',
242 default_toolset='v110'),
243 '2010': VisualStudioVersion('2010',
244 'Visual Studio 2010',
245 solution_version='11.00',
246 project_version='4.0',
250 sdk_based=sdk_based),
251 '2010e': VisualStudioVersion('2010e',
252 'Visual C++ Express 2010',
253 solution_version='11.00',
254 project_version='4.0',
258 sdk_based=sdk_based),
259 '2008': VisualStudioVersion('2008',
260 'Visual Studio 2008',
261 solution_version='10.00',
262 project_version='9.00',
266 sdk_based=sdk_based),
267 '2008e': VisualStudioVersion('2008e',
268 'Visual Studio 2008',
269 solution_version='10.00',
270 project_version='9.00',
274 sdk_based=sdk_based),
275 '2005': VisualStudioVersion('2005',
276 'Visual Studio 2005',
277 solution_version='9.00',
278 project_version='8.00',
282 sdk_based=sdk_based),
283 '2005e': VisualStudioVersion('2005e',
284 'Visual Studio 2005',
285 solution_version='9.00',
286 project_version='8.00',
290 sdk_based=sdk_based),
292 return versions[str(name)]
295 def _ConvertToCygpath(path):
296 """Convert to cygwin path if we are using cygwin."""
297 if sys.platform == 'cygwin':
298 p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
299 path = p.communicate()[0].strip()
303 def _DetectVisualStudioVersions(versions_to_check, force_express):
304 """Collect the list of installed visual studio versions.
307 A list of visual studio versions installed in descending order of
309 Base this on the registry and a quick check if devenv.exe exists.
310 Only versions 8-10 are considered.
312 2005(e) - Visual Studio 2005 (8)
313 2008(e) - Visual Studio 2008 (9)
314 2010(e) - Visual Studio 2010 (10)
315 2012(e) - Visual Studio 2012 (11)
316 2013(e) - Visual Studio 2013 (11)
317 Where (e) is e for express editions of MSVS and blank otherwise.
327 for version in versions_to_check:
328 # Old method of searching for which VS version is installed
329 # We don't use the 2010-encouraged-way because we also want to get the
330 # path to the binaries, which it doesn't offer.
331 keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
332 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
333 r'HKLM\Software\Microsoft\VCExpress\%s' % version,
334 r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
335 for index in range(len(keys)):
336 path = _RegistryGetValue(keys[index], 'InstallDir')
339 path = _ConvertToCygpath(path)
341 full_path = os.path.join(path, 'devenv.exe')
342 express_path = os.path.join(path, 'vcexpress.exe')
343 if not force_express and os.path.exists(full_path):
345 versions.append(_CreateVersion(version_to_year[version],
346 os.path.join(path, '..', '..')))
348 elif os.path.exists(express_path):
350 versions.append(_CreateVersion(version_to_year[version] + 'e',
351 os.path.join(path, '..', '..')))
353 # The old method above does not work when only SDK is installed.
354 keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
355 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
356 for index in range(len(keys)):
357 path = _RegistryGetValue(keys[index], version)
360 path = _ConvertToCygpath(path)
361 versions.append(_CreateVersion(version_to_year[version] + 'e',
362 os.path.join(path, '..'), sdk_based=True))
367 def SelectVisualStudioVersion(version='auto'):
368 """Select which version of Visual Studio projects to generate.
371 version: Hook to allow caller to force a particular version (vs auto).
373 An object representing a visual studio project format version.
375 # In auto mode, check environment variable for override.
376 if version == 'auto':
377 version = os.environ.get('GYP_MSVS_VERSION', 'auto')
379 'auto': ('10.0', '12.0', '9.0', '8.0', '11.0'),
391 override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
393 msvs_version = os.environ.get('GYP_MSVS_VERSION')
395 raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
396 'set to a particular version (e.g. 2010e).')
397 return _CreateVersion(msvs_version, override_path, sdk_based=True)
398 version = str(version)
399 versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
401 if version == 'auto':
402 # Default to 2005 if we couldn't find anything
403 return _CreateVersion('2005', None)
405 return _CreateVersion(version, None)