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