Initial commit
[yaffs-website] / node_modules / node-gyp / 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 self.short_name[-1] != 'e' and (
88             os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
89             os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
90           # VS2013 and later, non-Express have a x64-x86 cross that we want
91           # to prefer.
92           return [os.path.normpath(
93              os.path.join(self.path, 'VC/vcvarsall.bat')), 'amd64_x86']
94         # Otherwise, the standard x86 compiler.
95         return [os.path.normpath(
96           os.path.join(self.path, 'Common7/Tools/vsvars32.bat'))]
97       else:
98         assert target_arch == 'x64'
99         arg = 'x86_amd64'
100         # Use the 64-on-64 compiler if we're not using an express
101         # edition and we're running on a 64bit OS.
102         if self.short_name[-1] != 'e' and (
103             os.environ.get('PROCESSOR_ARCHITECTURE') == 'AMD64' or
104             os.environ.get('PROCESSOR_ARCHITEW6432') == 'AMD64'):
105           arg = 'amd64'
106         return [os.path.normpath(
107             os.path.join(self.path, 'VC/vcvarsall.bat')), arg]
108
109
110 def _RegistryQueryBase(sysdir, key, value):
111   """Use reg.exe to read a particular key.
112
113   While ideally we might use the win32 module, we would like gyp to be
114   python neutral, so for instance cygwin python lacks this module.
115
116   Arguments:
117     sysdir: The system subdirectory to attempt to launch reg.exe from.
118     key: The registry key to read from.
119     value: The particular value to read.
120   Return:
121     stdout from reg.exe, or None for failure.
122   """
123   # Skip if not on Windows or Python Win32 setup issue
124   if sys.platform not in ('win32', 'cygwin'):
125     return None
126   # Setup params to pass to and attempt to launch reg.exe
127   cmd = [os.path.join(os.environ.get('WINDIR', ''), sysdir, 'reg.exe'),
128          'query', key]
129   if value:
130     cmd.extend(['/v', value])
131   p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
132   # Obtain the stdout from reg.exe, reading to the end so p.returncode is valid
133   # Note that the error text may be in [1] in some cases
134   text = p.communicate()[0]
135   # Check return code from reg.exe; officially 0==success and 1==error
136   if p.returncode:
137     return None
138   return text
139
140
141 def _RegistryQuery(key, value=None):
142   r"""Use reg.exe to read a particular key through _RegistryQueryBase.
143
144   First tries to launch from %WinDir%\Sysnative to avoid WoW64 redirection. If
145   that fails, it falls back to System32.  Sysnative is available on Vista and
146   up and available on Windows Server 2003 and XP through KB patch 942589. Note
147   that Sysnative will always fail if using 64-bit python due to it being a
148   virtual directory and System32 will work correctly in the first place.
149
150   KB 942589 - http://support.microsoft.com/kb/942589/en-us.
151
152   Arguments:
153     key: The registry key.
154     value: The particular registry value to read (optional).
155   Return:
156     stdout from reg.exe, or None for failure.
157   """
158   text = None
159   try:
160     text = _RegistryQueryBase('Sysnative', key, value)
161   except OSError, e:
162     if e.errno == errno.ENOENT:
163       text = _RegistryQueryBase('System32', key, value)
164     else:
165       raise
166   return text
167
168
169 def _RegistryGetValueUsingWinReg(key, value):
170   """Use the _winreg module to obtain the value of a registry key.
171
172   Args:
173     key: The registry key.
174     value: The particular registry value to read.
175   Return:
176     contents of the registry key's value, or None on failure.  Throws
177     ImportError if _winreg is unavailable.
178   """
179   import _winreg
180   try:
181     root, subkey = key.split('\\', 1)
182     assert root == 'HKLM'  # Only need HKLM for now.
183     with _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, subkey) as hkey:
184       return _winreg.QueryValueEx(hkey, value)[0]
185   except WindowsError:
186     return None
187
188
189 def _RegistryGetValue(key, value):
190   """Use _winreg or reg.exe to obtain the value of a registry key.
191
192   Using _winreg is preferable because it solves an issue on some corporate
193   environments where access to reg.exe is locked down. However, we still need
194   to fallback to reg.exe for the case where the _winreg module is not available
195   (for example in cygwin python).
196
197   Args:
198     key: The registry key.
199     value: The particular registry value to read.
200   Return:
201     contents of the registry key's value, or None on failure.
202   """
203   try:
204     return _RegistryGetValueUsingWinReg(key, value)
205   except ImportError:
206     pass
207
208   # Fallback to reg.exe if we fail to import _winreg.
209   text = _RegistryQuery(key, value)
210   if not text:
211     return None
212   # Extract value.
213   match = re.search(r'REG_\w+\s+([^\r]+)\r\n', text)
214   if not match:
215     return None
216   return match.group(1)
217
218
219 def _CreateVersion(name, path, sdk_based=False):
220   """Sets up MSVS project generation.
221
222   Setup is based off the GYP_MSVS_VERSION environment variable or whatever is
223   autodetected if GYP_MSVS_VERSION is not explicitly specified. If a version is
224   passed in that doesn't match a value in versions python will throw a error.
225   """
226   if path:
227     path = os.path.normpath(path)
228   versions = {
229       '2015': VisualStudioVersion('2015',
230                                   'Visual Studio 2015',
231                                   solution_version='12.00',
232                                   project_version='14.0',
233                                   flat_sln=False,
234                                   uses_vcxproj=True,
235                                   path=path,
236                                   sdk_based=sdk_based,
237                                   default_toolset='v140'),
238       '2013': VisualStudioVersion('2013',
239                                   'Visual Studio 2013',
240                                   solution_version='13.00',
241                                   project_version='12.0',
242                                   flat_sln=False,
243                                   uses_vcxproj=True,
244                                   path=path,
245                                   sdk_based=sdk_based,
246                                   default_toolset='v120'),
247       '2013e': VisualStudioVersion('2013e',
248                                    'Visual Studio 2013',
249                                    solution_version='13.00',
250                                    project_version='12.0',
251                                    flat_sln=True,
252                                    uses_vcxproj=True,
253                                    path=path,
254                                    sdk_based=sdk_based,
255                                    default_toolset='v120'),
256       '2012': VisualStudioVersion('2012',
257                                   'Visual Studio 2012',
258                                   solution_version='12.00',
259                                   project_version='4.0',
260                                   flat_sln=False,
261                                   uses_vcxproj=True,
262                                   path=path,
263                                   sdk_based=sdk_based,
264                                   default_toolset='v110'),
265       '2012e': VisualStudioVersion('2012e',
266                                    'Visual Studio 2012',
267                                    solution_version='12.00',
268                                    project_version='4.0',
269                                    flat_sln=True,
270                                    uses_vcxproj=True,
271                                    path=path,
272                                    sdk_based=sdk_based,
273                                    default_toolset='v110'),
274       '2010': VisualStudioVersion('2010',
275                                   'Visual Studio 2010',
276                                   solution_version='11.00',
277                                   project_version='4.0',
278                                   flat_sln=False,
279                                   uses_vcxproj=True,
280                                   path=path,
281                                   sdk_based=sdk_based),
282       '2010e': VisualStudioVersion('2010e',
283                                    'Visual C++ Express 2010',
284                                    solution_version='11.00',
285                                    project_version='4.0',
286                                    flat_sln=True,
287                                    uses_vcxproj=True,
288                                    path=path,
289                                    sdk_based=sdk_based),
290       '2008': VisualStudioVersion('2008',
291                                   'Visual Studio 2008',
292                                   solution_version='10.00',
293                                   project_version='9.00',
294                                   flat_sln=False,
295                                   uses_vcxproj=False,
296                                   path=path,
297                                   sdk_based=sdk_based),
298       '2008e': VisualStudioVersion('2008e',
299                                    'Visual Studio 2008',
300                                    solution_version='10.00',
301                                    project_version='9.00',
302                                    flat_sln=True,
303                                    uses_vcxproj=False,
304                                    path=path,
305                                    sdk_based=sdk_based),
306       '2005': VisualStudioVersion('2005',
307                                   'Visual Studio 2005',
308                                   solution_version='9.00',
309                                   project_version='8.00',
310                                   flat_sln=False,
311                                   uses_vcxproj=False,
312                                   path=path,
313                                   sdk_based=sdk_based),
314       '2005e': VisualStudioVersion('2005e',
315                                    'Visual Studio 2005',
316                                    solution_version='9.00',
317                                    project_version='8.00',
318                                    flat_sln=True,
319                                    uses_vcxproj=False,
320                                    path=path,
321                                    sdk_based=sdk_based),
322   }
323   return versions[str(name)]
324
325
326 def _ConvertToCygpath(path):
327   """Convert to cygwin path if we are using cygwin."""
328   if sys.platform == 'cygwin':
329     p = subprocess.Popen(['cygpath', path], stdout=subprocess.PIPE)
330     path = p.communicate()[0].strip()
331   return path
332
333
334 def _DetectVisualStudioVersions(versions_to_check, force_express):
335   """Collect the list of installed visual studio versions.
336
337   Returns:
338     A list of visual studio versions installed in descending order of
339     usage preference.
340     Base this on the registry and a quick check if devenv.exe exists.
341     Only versions 8-10 are considered.
342     Possibilities are:
343       2005(e) - Visual Studio 2005 (8)
344       2008(e) - Visual Studio 2008 (9)
345       2010(e) - Visual Studio 2010 (10)
346       2012(e) - Visual Studio 2012 (11)
347       2013(e) - Visual Studio 2013 (12)
348       2015    - Visual Studio 2015 (14)
349     Where (e) is e for express editions of MSVS and blank otherwise.
350   """
351   version_to_year = {
352       '8.0': '2005',
353       '9.0': '2008',
354       '10.0': '2010',
355       '11.0': '2012',
356       '12.0': '2013',
357       '14.0': '2015',
358   }
359   versions = []
360   for version in versions_to_check:
361     # Old method of searching for which VS version is installed
362     # We don't use the 2010-encouraged-way because we also want to get the
363     # path to the binaries, which it doesn't offer.
364     keys = [r'HKLM\Software\Microsoft\VisualStudio\%s' % version,
365             r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\%s' % version,
366             r'HKLM\Software\Microsoft\VCExpress\%s' % version,
367             r'HKLM\Software\Wow6432Node\Microsoft\VCExpress\%s' % version]
368     for index in range(len(keys)):
369       path = _RegistryGetValue(keys[index], 'InstallDir')
370       if not path:
371         continue
372       path = _ConvertToCygpath(path)
373       # Check for full.
374       full_path = os.path.join(path, 'devenv.exe')
375       express_path = os.path.join(path, '*express.exe')
376       if not force_express and os.path.exists(full_path):
377         # Add this one.
378         versions.append(_CreateVersion(version_to_year[version],
379             os.path.join(path, '..', '..')))
380       # Check for express.
381       elif glob.glob(express_path):
382         # Add this one.
383         versions.append(_CreateVersion(version_to_year[version] + 'e',
384             os.path.join(path, '..', '..')))
385
386     # The old method above does not work when only SDK is installed.
387     keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
388             r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
389     for index in range(len(keys)):
390       path = _RegistryGetValue(keys[index], version)
391       if not path:
392         continue
393       path = _ConvertToCygpath(path)
394       if version != '14.0':  # There is no Express edition for 2015.
395         versions.append(_CreateVersion(version_to_year[version] + 'e',
396             os.path.join(path, '..'), sdk_based=True))
397
398   return versions
399
400
401 def SelectVisualStudioVersion(version='auto', allow_fallback=True):
402   """Select which version of Visual Studio projects to generate.
403
404   Arguments:
405     version: Hook to allow caller to force a particular version (vs auto).
406   Returns:
407     An object representing a visual studio project format version.
408   """
409   # In auto mode, check environment variable for override.
410   if version == 'auto':
411     version = os.environ.get('GYP_MSVS_VERSION', 'auto')
412   version_map = {
413     'auto': ('14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
414     '2005': ('8.0',),
415     '2005e': ('8.0',),
416     '2008': ('9.0',),
417     '2008e': ('9.0',),
418     '2010': ('10.0',),
419     '2010e': ('10.0',),
420     '2012': ('11.0',),
421     '2012e': ('11.0',),
422     '2013': ('12.0',),
423     '2013e': ('12.0',),
424     '2015': ('14.0',),
425   }
426   override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
427   if override_path:
428     msvs_version = os.environ.get('GYP_MSVS_VERSION')
429     if not msvs_version:
430       raise ValueError('GYP_MSVS_OVERRIDE_PATH requires GYP_MSVS_VERSION to be '
431                        'set to a particular version (e.g. 2010e).')
432     return _CreateVersion(msvs_version, override_path, sdk_based=True)
433   version = str(version)
434   versions = _DetectVisualStudioVersions(version_map[version], 'e' in version)
435   if not versions:
436     if not allow_fallback:
437       raise ValueError('Could not locate Visual Studio installation.')
438     if version == 'auto':
439       # Default to 2005 if we couldn't find anything
440       return _CreateVersion('2005', None)
441     else:
442       return _CreateVersion(version, None)
443   return versions[0]