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 # Use the 64-on-64 compiler if we're not using an express
100 # edition and we're running on a 64bit OS.
101 if self.short_name[-1] != 'e' and (
102 os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
103 os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
105 return [os.path.normpath(
106 os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
109 def _RegistryQueryBase(sysdir, key, value):
110 """Use reg.exe to read a particular key.
112 While ideally we might use the win32 module, we would like gyp to be
113 python neutral, so for instance cygwin python lacks this module.
116 sysdir: The system subdirectory to attempt to launch reg.exe from.
117 key: The registry key to read from.
118 value: The particular value to read.
120 stdout from reg.exe, or None for failure.
122 # Skip if not on Windows or Python Win32 setup issue
123 if sys.platform not in ('win32', 'cygwin'):
125 # Setup params to pass to and attempt to launch reg.exe
126 cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
129 cmd.extend(['/v', value])
130 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
131 # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
132 # Note that the error text may be in [1] in some cases
133 text = p.communicate()[0]
134 # Check return code from reg.exe; officially 0==success and 1==error
140 def _RegistryQuery(key, value=None):
141 """Use reg.exe to read a particular key through _RegistryQueryBase.
143 First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
144 that fails, it falls back to System32. Sysnative is available on Vista and
145 up and available on Windows Server 2003 and XP through KB patch 942589. Note
146 that Sysnative will always fail if using 64-bit python due to it being a
147 virtual directory and System32 will work correctly in the first place.
149 KB 942589 - http://support.microsoft.com/kb/942589/en-us.
152 key: The registry key.
153 value: The particular registry value to read (optional).
155 stdout from reg.exe, or None for failure.
159 text = _RegistryQueryBase('Sysnative', key, value)
161 if e.errno == errno.ENOENT:
162 text = _RegistryQueryBase('System32', key, value)
168 def _RegistryGetValue(key, value):
169 """Use reg.exe to obtain the value of a registry key.
172 key: The registry key.
173 value: The particular registry value to read.
175 contents of the registry key's value, or None on failure.
177 text = _RegistryQuery(key, value)
181 match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
184 return match.group(1)
187 def _RegistryKeyExists(key):
188 """Use reg.exe to see if a key exists.
191 key: The registry key to check.
193 True if the key exists
195 if not _RegistryQuery(key):
200 def _CreateVersion(name, path, sdk_based=False):
201 """Sets up MSVS project generation.
203 Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
204 autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
205 passed in that doesn't match a value in versions python will throw a error.
208 path = os.path.normpath(path)
210 '2013': VisualStudioVersion('2013',
211 'Visual Studio 2013',
212 solution_version='13.00',
213 project_version='12.0',
218 default_toolset='v120'),
219 '2013e': VisualStudioVersion('2013e',
220 'Visual Studio 2013',
221 solution_version='13.00',
222 project_version='12.0',
227 default_toolset='v120'),
228 '2012': VisualStudioVersion('2012',
229 'Visual Studio 2012',
230 solution_version='12.00',
231 project_version='4.0',
236 default_toolset='v110'),
237 '2012e': VisualStudioVersion('2012e',
238 'Visual Studio 2012',
239 solution_version='12.00',
240 project_version='4.0',
245 default_toolset='v110'),
246 '2010': VisualStudioVersion('2010',
247 'Visual Studio 2010',
248 solution_version='11.00',
249 project_version='4.0',
253 sdk_based=sdk_based),
254 '2010e': VisualStudioVersion('2010e',
255 'Visual C++ Express 2010',
256 solution_version='11.00',
257 project_version='4.0',
261 sdk_based=sdk_based),
262 '2008': VisualStudioVersion('2008',
263 'Visual Studio 2008',
264 solution_version='10.00',
265 project_version='9.00',
269 sdk_based=sdk_based),
270 '2008e': VisualStudioVersion('2008e',
271 'Visual Studio 2008',
272 solution_version='10.00',
273 project_version='9.00',
277 sdk_based=sdk_based),
278 '2005': VisualStudioVersion('2005',
279 'Visual Studio 2005',
280 solution_version='9.00',
281 project_version='8.00',
285 sdk_based=sdk_based),
286 '2005e': VisualStudioVersion('2005e',
287 'Visual Studio 2005',
288 solution_version='9.00',
289 project_version='8.00',
293 sdk_based=sdk_based),
295 return versions[str(name)]
298 def _ConvertToCygpath(path):
299 """Convert to cygwin path if we are using cygwin."""
300 if sys.platform == 'cygwin':
301 p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
302 path = p.communicate()[0].strip()
306 def _DetectVisualStudioVersions(versions_to_check, force_express):
307 """Collect the list of installed visual studio versions.
310 A list of visual studio versions installed in descending order of
312 Base this on the registry and a quick check if devenv.exe exists.
313 Only versions 8-10 are considered.
315 2005(e) - Visual Studio 2005 (8)
316 2008(e) - Visual Studio 2008 (9)
317 2010(e) - Visual Studio 2010 (10)
318 2012(e) - Visual Studio 2012 (11)
319 2013(e) - Visual Studio 2013 (11)
320 Where (e) is e for express editions of MSVS and blank otherwise.
330 for version in versions_to_check:
331 # Old method of searching for which VS version is installed
332 # We don't use the 2010-encouraged-way because we also want to get the
333 # path to the binaries, which it doesn't offer.
334 keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
335 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
336 r'HKLM\Software\Microsoft\VCExpress\%s' % version,
337 r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
338 for index in range(len(keys)):
339 path = _RegistryGetValue(keys[index], 'InstallDir')
342 path = _ConvertToCygpath(path)
344 full_path = os.path.join(path, 'devenv.exe')
345 express_path = os.path.join(path, '*express.exe')
346 if not force_express and os.path.exists(full_path):
348 versions.append(_CreateVersion(version_to_year[version],
349 os.path.join(path, '..', '..')))
351 elif glob.glob(express_path):
353 versions.append(_CreateVersion(version_to_year[version] + 'e',
354 os.path.join(path, '..', '..')))
356 # The old method above does not work when only SDK is installed.
357 keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
358 r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
359 for index in range(len(keys)):
360 path = _RegistryGetValue(keys[index], version)
363 path = _ConvertToCygpath(path)
364 versions.append(_CreateVersion(version_to_year[version] + 'e',
365 os.path.join(path, '..'), sdk_based=True))
370 def SelectVisualStudioVersion(version='auto'):
371 """Select which version of Visual Studio projects to generate.
374 version: Hook to allow caller to force a particular version (vs auto).
376 An object representing a visual studio project format version.
378 # In auto mode, check environment variable for override.
379 if version == 'auto':
380 version = os.environ.get('GYP_MSVS_VERSION', 'auto')
382 'auto': ('10.0', '12.0', '9.0', '8.0', '11.0'),
394 override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
396 msvs_version = os.environ.get('GYP_MSVS_VERSION')
398 raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
399 'set to a particular version (e.g. 2010e).')
400 return _CreateVersion(msvs_version, override_path, sdk_based=True)
401 version = str(version)
402 versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
404 if version == 'auto':
405 # Default to 2005 if we couldn't find anything
406 return _CreateVersion('2005', None)
408 return _CreateVersion(version, None)