956fa011610966e3fbe471c2dcf821e6e4fd8c80
[platform/upstream/nodejs.git] / tools / gyp / pylib / gyp / MSVSVersion.py
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.
4
5 """Handle version information related to Visual Stuio."""
6
7 import errno
8 import os
9 import re
10 import subprocess
11 import sys
12 import gyp
13
14
15 class VisualStudioVersion(object):
16   """Information regarding a version of Visual Studio."""
17
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
27     self.path = path
28     self.sdk_based = sdk_based
29     self.default_toolset = default_toolset
30
31   def ShortName(self):
32     return self.short_name
33
34   def Description(self):
35     """Get the full description of the version."""
36     return self.description
37
38   def SolutionVersion(self):
39     """Get the version number of the sln files."""
40     return self.solution_version
41
42   def ProjectVersion(self):
43     """Get the version number of the vcproj or vcxproj files."""
44     return self.project_version
45
46   def FlatSolution(self):
47     return self.flat_sln
48
49   def UsesVcxproj(self):
50     """Returns true if this version uses a vcxproj file."""
51     return self.uses_vcxproj
52
53   def ProjectExtension(self):
54     """Returns the file extension for the project."""
55     return self.uses_vcxproj and '.vcxproj' or '.vcproj'
56
57   def Path(self):
58     """Returns the path to Visual Studio installation."""
59     return self.path
60
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))
64
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
69
70   def SetupScript(self, target_arch):
71     """Returns a command (with arguments) to be used to set up the
72     environment."""
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
75     # 'x86' or 'x64'.
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')),
80               '/' + target_arch]
81     else:
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
84       # isn't always.
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'))]
95       else:
96         assert target_arch == 'x64'
97         arg = 'x86_amd64'
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.
101           arg = 'amd64'
102         return [os.path.normpath(
103             os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
104
105
106 def _RegistryQueryBase(sysdir, key, value):
107   """Use reg.exe to read a particular key.
108
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.
111
112   Arguments:
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.
116   Return:
117     stdout from reg.exe, or None for failure.
118   """
119   # Skip if not on Windows or Python Win32 setup issue
120   if sys.platform not in ('win32', 'cygwin'):
121     return None
122   # Setup params to pass to and attempt to launch reg.exe
123   cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
124          'query', key]
125   if value:
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
132   if p.returncode:
133     return None
134   return text
135
136
137 def _RegistryQuery(key, value=None):
138   """Use reg.exe to read a particular key through _RegistryQueryBase.
139
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.
145
146   KB 942589 - http://support.microsoft.com/kb/942589/en-us.
147
148   Arguments:
149     key: The registry key.
150     value: The particular registry value to read (optional).
151   Return:
152     stdout from reg.exe, or None for failure.
153   """
154   text = None
155   try:
156     text = _RegistryQueryBase('Sysnative', key, value)
157   except OSError, e:
158     if e.errno == errno.ENOENT:
159       text = _RegistryQueryBase('System32', key, value)
160     else:
161       raise
162   return text
163
164
165 def _RegistryGetValue(key, value):
166   """Use reg.exe to obtain the value of a registry key.
167
168   Args:
169     key: The registry key.
170     value: The particular registry value to read.
171   Return:
172     contents of the registry key's value, or None on failure.
173   """
174   text = _RegistryQuery(key, value)
175   if not text:
176     return None
177   # Extract value.
178   match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
179   if not match:
180     return None
181   return match.group(1)
182
183
184 def _RegistryKeyExists(key):
185   """Use reg.exe to see if a key exists.
186
187   Args:
188     key: The registry key to check.
189   Return:
190     True if the key exists
191   """
192   if not _RegistryQuery(key):
193     return False
194   return True
195
196
197 def _CreateVersion(name, path, sdk_based=False):
198   """Sets up MSVS project generation.
199
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.
203   """
204   if path:
205     path = os.path.normpath(path)
206   versions = {
207       '2013': VisualStudioVersion('2013',
208                                   'Visual Studio 2013',
209                                   solution_version='13.00',
210                                   project_version='12.0',
211                                   flat_sln=False,
212                                   uses_vcxproj=True,
213                                   path=path,
214                                   sdk_based=sdk_based,
215                                   default_toolset='v120'),
216       '2013e': VisualStudioVersion('2013e',
217                                    'Visual Studio 2013',
218                                    solution_version='13.00',
219                                    project_version='12.0',
220                                    flat_sln=True,
221                                    uses_vcxproj=True,
222                                    path=path,
223                                    sdk_based=sdk_based,
224                                    default_toolset='v120'),
225       '2012': VisualStudioVersion('2012',
226                                   'Visual Studio 2012',
227                                   solution_version='12.00',
228                                   project_version='4.0',
229                                   flat_sln=False,
230                                   uses_vcxproj=True,
231                                   path=path,
232                                   sdk_based=sdk_based,
233                                   default_toolset='v110'),
234       '2012e': VisualStudioVersion('2012e',
235                                    'Visual Studio 2012',
236                                    solution_version='12.00',
237                                    project_version='4.0',
238                                    flat_sln=True,
239                                    uses_vcxproj=True,
240                                    path=path,
241                                    sdk_based=sdk_based,
242                                    default_toolset='v110'),
243       '2010': VisualStudioVersion('2010',
244                                   'Visual Studio 2010',
245                                   solution_version='11.00',
246                                   project_version='4.0',
247                                   flat_sln=False,
248                                   uses_vcxproj=True,
249                                   path=path,
250                                   sdk_based=sdk_based),
251       '2010e': VisualStudioVersion('2010e',
252                                    'Visual C++ Express 2010',
253                                    solution_version='11.00',
254                                    project_version='4.0',
255                                    flat_sln=True,
256                                    uses_vcxproj=True,
257                                    path=path,
258                                    sdk_based=sdk_based),
259       '2008': VisualStudioVersion('2008',
260                                   'Visual Studio 2008',
261                                   solution_version='10.00',
262                                   project_version='9.00',
263                                   flat_sln=False,
264                                   uses_vcxproj=False,
265                                   path=path,
266                                   sdk_based=sdk_based),
267       '2008e': VisualStudioVersion('2008e',
268                                    'Visual Studio 2008',
269                                    solution_version='10.00',
270                                    project_version='9.00',
271                                    flat_sln=True,
272                                    uses_vcxproj=False,
273                                    path=path,
274                                    sdk_based=sdk_based),
275       '2005': VisualStudioVersion('2005',
276                                   'Visual Studio 2005',
277                                   solution_version='9.00',
278                                   project_version='8.00',
279                                   flat_sln=False,
280                                   uses_vcxproj=False,
281                                   path=path,
282                                   sdk_based=sdk_based),
283       '2005e': VisualStudioVersion('2005e',
284                                    'Visual Studio 2005',
285                                    solution_version='9.00',
286                                    project_version='8.00',
287                                    flat_sln=True,
288                                    uses_vcxproj=False,
289                                    path=path,
290                                    sdk_based=sdk_based),
291   }
292   return versions[str(name)]
293
294
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()
300   return path
301
302
303 def _DetectVisualStudioVersions(versions_to_check, force_express):
304   """Collect the list of installed visual studio versions.
305
306   Returns:
307     A list of visual studio versions installed in descending order of
308     usage preference.
309     Base this on the registry and a quick check if devenv.exe exists.
310     Only versions 8-10 are considered.
311     Possibilities are:
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.
318   """
319   version_to_year = {
320       '8.0': '2005',
321       '9.0': '2008',
322       '10.0': '2010',
323       '11.0': '2012',
324       '12.0': '2013',
325   }
326   versions = []
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')
337       if not path:
338         continue
339       path = _ConvertToCygpath(path)
340       # Check for full.
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):
344         # Add this one.
345         versions.append(_CreateVersion(version_to_year[version],
346             os.path.join(path, '..', '..')))
347       # Check for express.
348       elif os.path.exists(express_path):
349         # Add this one.
350         versions.append(_CreateVersion(version_to_year[version] + 'e',
351             os.path.join(path, '..', '..')))
352
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)
358       if not path:
359         continue
360       path = _ConvertToCygpath(path)
361       versions.append(_CreateVersion(version_to_year[version] + 'e',
362           os.path.join(path, '..'), sdk_based=True))
363
364   return versions
365
366
367 def SelectVisualStudioVersion(version='auto'):
368   """Select which version of Visual Studio projects to generate.
369
370   Arguments:
371     version: Hook to allow caller to force a particular version (vs auto).
372   Returns:
373     An object representing a visual studio project format version.
374   """
375   # In auto mode, check environment variable for override.
376   if version == 'auto':
377     version = os.environ.get('GYP_MSVS_VERSION', 'auto')
378   version_map = {
379     'auto': ('10.0', '12.0', '9.0', '8.0', '11.0'),
380     '2005': ('8.0',),
381     '2005e': ('8.0',),
382     '2008': ('9.0',),
383     '2008e': ('9.0',),
384     '2010': ('10.0',),
385     '2010e': ('10.0',),
386     '2012': ('11.0',),
387     '2012e': ('11.0',),
388     '2013': ('12.0',),
389     '2013e': ('12.0',),
390   }
391   override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
392   if override_path:
393     msvs_version = os.environ.get('GYP_MSVS_VERSION')
394     if not 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)
400   if not versions:
401     if version == 'auto':
402       # Default to 2005 if we couldn't find anything
403       return _CreateVersion('2005', None)
404     else:
405       return _CreateVersion(version, None)
406   return versions[0]