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 (
88 os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
89 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
90 # VS2013 non-Express has a x64-x86 cross that we want to prefer.
91 return [os.path.normpath(
92 os.path.join(self.path, 'VC/vcvarsall.bat')), 'amd64_x86']
93 # Otherwise, the standard x86 compiler.
94 return [os.path.normpath(
95 os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
97 assert target_arch == 'x64'
99 if (os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
100 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
101 # Use the 64-on-64 compiler if we can.
103 return [os.path.normpath(
104 os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
107 def _RegistryQueryBase(sysdir, key, value):
108 """Use reg.exe to read a particular key.
110 While ideally we might use the win32 module, we would like gyp to be
111 python neutral, so for instance cygwin python lacks this module.
114 sysdir: The system subdirectory to attempt to launch reg.exe from.
115 key: The registry key to read from.
116 value: The particular value to read.
118 stdout from reg.exe, or None for failure.
120 # Skip if not on Windows or Python Win32 setup issue
121 if sys.platform not in ('win32', 'cygwin'):
123 # Setup params to pass to and attempt to launch reg.exe
124 cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
127 cmd.extend(['/v', value])
128 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
129 # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
130 # Note that the error text may be in [1] in some cases
131 text = p.communicate()[0]
132 # Check return code from reg.exe; officially 0==success and 1==error
138 def _RegistryQuery(key, value=None):
139 """Use reg.exe to read a particular key through _RegistryQueryBase.
141 First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
142 that fails, it falls back to System32. Sysnative is available on Vista and
143 up and available on Windows Server 2003 and XP through KB patch 942589. Note
144 that Sysnative will always fail if using 64-bit python due to it being a
145 virtual directory and System32 will work correctly in the first place.
147 KB 942589 - http://support.microsoft.com/kb/942589/en-us.
150 key: The registry key.
151 value: The particular registry value to read (optional).
153 stdout from reg.exe, or None for failure.
157 text = _RegistryQueryBase('Sysnative', key, value)
159 if e.errno == errno.ENOENT:
160 text = _RegistryQueryBase('System32', key, value)
166 def _RegistryGetValue(key, value):
167 """Use reg.exe to obtain the value of a registry key.
170 key: The registry key.
171 value: The particular registry value to read.
173 contents of the registry key's value, or None on failure.
175 text = _RegistryQuery(key, value)
179 match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
182 return match.group(1)
185 def _RegistryKeyExists(key):
186 """Use reg.exe to see if a key exists.
189 key: The registry key to check.
191 True if the key exists
193 if not _RegistryQuery(key):
198 def _CreateVersion(name, path, sdk_based=False):
199 """Sets up MSVS project generation.
201 Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
202 autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
203 passed in that doesn't match a value in versions python will throw a error.
206 path = os.path.normpath(path)
208 '2013': VisualStudioVersion('2013',
209 'Visual Studio 2013',
210 solution_version='13.00',
211 project_version='12.0',
216 default_toolset='v120'),
217 '2013e': VisualStudioVersion('2013e',
218 'Visual Studio 2013',
219 solution_version='13.00',
220 project_version='12.0',
225 default_toolset='v120'),
226 '2012': VisualStudioVersion('2012',
227 'Visual Studio 2012',
228 solution_version='12.00',
229 project_version='4.0',
234 default_toolset='v110'),
235 '2012e': VisualStudioVersion('2012e',
236 'Visual Studio 2012',
237 solution_version='12.00',
238 project_version='4.0',
243 default_toolset='v110'),
244 '2010': VisualStudioVersion('2010',
245 'Visual Studio 2010',
246 solution_version='11.00',
247 project_version='4.0',
251 sdk_based=sdk_based),
252 '2010e': VisualStudioVersion('2010e',
253 'Visual C++ Express 2010',
254 solution_version='11.00',
255 project_version='4.0',
259 sdk_based=sdk_based),
260 '2008': VisualStudioVersion('2008',
261 'Visual Studio 2008',
262 solution_version='10.00',
263 project_version='9.00',
267 sdk_based=sdk_based),
268 '2008e': VisualStudioVersion('2008e',
269 'Visual Studio 2008',
270 solution_version='10.00',
271 project_version='9.00',
275 sdk_based=sdk_based),
276 '2005': VisualStudioVersion('2005',
277 'Visual Studio 2005',
278 solution_version='9.00',
279 project_version='8.00',
283 sdk_based=sdk_based),
284 '2005e': VisualStudioVersion('2005e',
285 'Visual Studio 2005',
286 solution_version='9.00',
287 project_version='8.00',
291 sdk_based=sdk_based),
293 return versions[str(name)]
296 def _ConvertToCygpath(path):
297 """Convert to cygwin path if we are using cygwin."""
298 if sys.platform == 'cygwin':
299 p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
300 path = p.communicate()[0].strip()
304 def _DetectVisualStudioVersions(versions_to_check, force_express):
305 """Collect the list of installed visual studio versions.
308 A list of visual studio versions installed in descending order of
310 Base this on the registry and a quick check if devenv.exe exists.
311 Only versions 8-10 are considered.
313 2005(e) - Visual Studio 2005 (8)
314 2008(e) - Visual Studio 2008 (9)
315 2010(e) - Visual Studio 2010 (10)
316 2012(e) - Visual Studio 2012 (11)
317 2013(e) - Visual Studio 2013 (11)
318 Where (e) is e for express editions of MSVS and blank otherwise.
328 for version in versions_to_check:
329 # Old method of searching for which VS version is installed
330 # We don't use the 2010-encouraged-way because we also want to get the
331 # path to the binaries, which it doesn't offer.
332 keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
333 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
334 r'HKLM\Software\Microsoft\VCExpress\%s' % version,
335 r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
336 for index in range(len(keys)):
337 path = _RegistryGetValue(keys[index], 'InstallDir')
340 path = _ConvertToCygpath(path)
342 full_path = os.path.join(path, 'devenv.exe')
343 express_path = os.path.join(path, '*express.exe')
344 if not force_express and os.path.exists(full_path):
346 versions.append(_CreateVersion(version_to_year[version],
347 os.path.join(path, '..', '..')))
349 elif glob.glob(express_path):
351 versions.append(_CreateVersion(version_to_year[version] + 'e',
352 os.path.join(path, '..', '..')))
354 # The old method above does not work when only SDK is installed.
355 keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
356 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
357 for index in range(len(keys)):
358 path = _RegistryGetValue(keys[index], version)
361 path = _ConvertToCygpath(path)
362 versions.append(_CreateVersion(version_to_year[version] + 'e',
363 os.path.join(path, '..'), sdk_based=True))
368 def SelectVisualStudioVersion(version='auto'):
369 """Select which version of Visual Studio projects to generate.
372 version: Hook to allow caller to force a particular version (vs auto).
374 An object representing a visual studio project format version.
376 # In auto mode, check environment variable for override.
377 if version == 'auto':
378 version = os.environ.get('GYP_MSVS_VERSION', 'auto')
380 'auto': ('10.0', '12.0', '9.0', '8.0', '11.0'),
392 override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
394 msvs_version = os.environ.get('GYP_MSVS_VERSION')
396 raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
397 'set to a particular version (e.g. 2010e).')
398 return _CreateVersion(msvs_version, override_path, sdk_based=True)
399 version = str(version)
400 versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
402 if version == 'auto':
403 # Default to 2005 if we couldn't find anything
404 return _CreateVersion('2005', None)
406 return _CreateVersion(version, None)