264166f67b283f3a0eed0807a33f492ff7fe606b
[scons.git] / src / engine / SCons / Tool / MSCommon / vs.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 __doc__ = """Module to detect Visual Studio and/or Visual C/C++
27 """
28
29 import os
30
31 import SCons.Errors
32 import SCons.Util
33
34 from common import debug, \
35                    get_output, \
36                    is_win64, \
37                    normalize_env, \
38                    parse_output, \
39                    read_reg
40
41 import SCons.Tool.MSCommon.vc
42
43 class VisualStudio:
44     """
45     An abstract base class for trying to find installed versions of
46     Visual Studio.
47     """
48     def __init__(self, version, **kw):
49         self.version = version
50         kw['vc_version']  = kw.get('vc_version', version)
51         kw['sdk_version'] = kw.get('sdk_version', version)
52         self.__dict__.update(kw)
53         self._cache = {}
54
55     #
56
57     def find_batch_file(self):
58         vs_dir = self.get_vs_dir()
59         if not vs_dir:
60             debug('find_executable():  no vs_dir')
61             return None
62         batch_file = os.path.join(vs_dir, self.batch_file_path)
63         batch_file = os.path.normpath(batch_file)
64         if not os.path.isfile(batch_file):
65             debug('find_batch_file():  %s not on file system' % batch_file)
66             return None
67         return batch_file
68
69     def find_vs_dir_by_vc(self):
70         SCons.Tool.MSCommon.vc.get_installed_vcs()
71         dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version)
72         if not dir:
73             debug('find_vs_dir():  no installed VC %s' % self.vc_version)
74             return None
75         return dir
76         
77     def find_vs_dir_by_reg(self):
78         root = 'Software\\'
79
80         if is_win64():
81             root = root + 'Wow6432Node\\'
82         for key in self.hkeys:
83             if key=='use_dir':
84                 return self.find_vs_dir_by_vc()
85             key = root + key
86             try:
87                 comps = read_reg(key)
88             except WindowsError, e:
89                 debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key))
90             else:
91                 debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps)
92                 return comps
93         return None
94     
95     def find_vs_dir(self):
96         """ Can use registry or location of VC to find vs dir
97         First try to find by registry, and if that fails find via VC dir
98         """
99         
100         
101         if True:
102             vs_dir=self.find_vs_dir_by_reg()
103             return vs_dir
104         else:
105             return self.find_vs_dir_by_vc()
106
107     def find_executable(self):
108         vs_dir = self.get_vs_dir()
109         if not vs_dir:
110             debug('find_executable():  no vs_dir (%s)'%vs_dir)
111             return None
112         executable = os.path.join(vs_dir, self.executable_path)
113         executable = os.path.normpath(executable)
114         if not os.path.isfile(executable):
115             debug('find_executable():  %s not on file system' % executable)
116             return None
117         return executable
118     
119     #
120
121     def get_batch_file(self):
122         try:
123             return self._cache['batch_file']
124         except KeyError:
125             batch_file = self.find_batch_file()
126             self._cache['batch_file'] = batch_file
127             return batch_file
128
129     def get_executable(self):
130         try:
131             debug('get_executable using cache:%s'%self._cache['executable'])
132             return self._cache['executable']
133         except KeyError:
134             executable = self.find_executable()
135             self._cache['executable'] = executable
136             debug('get_executable not in cache:%s'%executable)
137             return executable
138
139     def get_vs_dir(self):
140         try:
141             return self._cache['vs_dir']
142         except KeyError:
143             vs_dir = self.find_vs_dir()
144             self._cache['vs_dir'] = vs_dir
145             return vs_dir
146
147     def get_supported_arch(self):
148         try:
149             return self._cache['supported_arch']
150         except KeyError:
151             # RDEVE: for the time being use hardcoded lists
152             # supported_arch = self.find_supported_arch()
153             self._cache['supported_arch'] = self.supported_arch
154             return self.supported_arch
155
156     def reset(self):
157         self._cache = {}
158
159 # The list of supported Visual Studio versions we know how to detect.
160 #
161 # How to look for .bat file ?
162 #  - VS 2008 Express (x86):
163 #     * from registry key productdir, gives the full path to vsvarsall.bat. In
164 #     HKEY_LOCAL_MACHINE):
165 #         Software\Microsoft\VCEpress\9.0\Setup\VC\productdir
166 #     * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC
167 #     relatively to the path given by the variable.
168 #
169 #  - VS 2008 Express (WoW6432: 32 bits on windows x64):
170 #         Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir
171 #
172 #  - VS 2005 Express (x86):
173 #     * from registry key productdir, gives the full path to vsvarsall.bat. In
174 #     HKEY_LOCAL_MACHINE):
175 #         Software\Microsoft\VCEpress\8.0\Setup\VC\productdir
176 #     * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC
177 #     relatively to the path given by the variable.
178 #
179 #  - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a
180 #  productdir ?
181 #
182 #  - VS 2003 .Net (pro edition ? x86):
183 #     * from registry key productdir. The path is then ..\Common7\Tools\
184 #     relatively to the key. The key is in HKEY_LOCAL_MACHINE):
185 #         Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir
186 #     * from environmnent variable VS71COMNTOOLS: the path is the full path to
187 #     vsvars32.bat
188 #
189 #  - VS 98 (VS 6):
190 #     * from registry key productdir. The path is then Bin
191 #     relatively to the key. The key is in HKEY_LOCAL_MACHINE):
192 #         Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir
193 #
194 # The first version found in the list is the one used by default if
195 # there are multiple versions installed.  Barring good reasons to
196 # the contrary, this means we should list versions from most recent
197 # to oldest.  Pro versions get listed before Express versions on the
198 # assumption that, by default, you'd rather use the version you paid
199 # good money for in preference to whatever Microsoft makes available
200 # for free.
201 #
202 # If you update this list, update the documentation in Tool/msvs.xml.
203
204 SupportedVSList = [
205     # Visual Studio 2010
206     # TODO: find the settings, perhaps from someone with a CTP copy?
207     #VisualStudio('TBD',
208     #             hkey_root=r'TBD',
209     #             common_tools_var='TBD',
210     #             executable_path=r'TBD',
211     #             default_dirname='TBD',
212     #),
213
214     # Visual Studio 2008
215     # The batch file we look for is in the VC directory,
216     # so the devenv.com executable is up in ..\..\Common7\IDE.
217     VisualStudio('9.0',
218                  sdk_version='6.1',
219                  hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'],
220                  common_tools_var='VS90COMNTOOLS',
221                  executable_path=r'Common7\IDE\devenv.com',
222                  batch_file_path=r'Common7\Tools\vsvars32.bat',
223                  default_dirname='Microsoft Visual Studio 9',
224                  supported_arch=['x86', 'amd64'],
225     ),
226
227     # Visual C++ 2008 Express Edition
228     # The batch file we look for is in the VC directory,
229     # so the VCExpress.exe executable is up in ..\..\Common7\IDE.
230     VisualStudio('9.0Exp',
231                  vc_version='9.0',
232                  sdk_version='6.1',
233                  hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'],
234                  common_tools_var='VS90COMNTOOLS',
235                  executable_path=r'Common7\IDE\VCExpress.exe',
236                  batch_file_path=r'Common7\Tools\vsvars32.bat',
237                  default_dirname='Microsoft Visual Studio 9',
238                  supported_arch=['x86'],
239     ),
240
241     # Visual Studio 2005
242     # The batch file we look for is in the VC directory,
243     # so the devenv.com executable is up in ..\..\Common7\IDE.
244     VisualStudio('8.0',
245                  sdk_version='6.0A',
246                  hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'],
247                  common_tools_var='VS80COMNTOOLS',
248                  executable_path=r'Common7\IDE\devenv.com',
249                  batch_file_path=r'Common7\Tools\vsvars32.bat',
250                  default_dirname='Microsoft Visual Studio 8',
251                  supported_arch=['x86', 'amd64'],
252     ),
253
254     # Visual C++ 2005 Express Edition
255     # The batch file we look for is in the VC directory,
256     # so the VCExpress.exe executable is up in ..\..\Common7\IDE.
257     VisualStudio('8.0Exp',
258                  vc_version='8.0Exp',
259                  sdk_version='6.0A',
260                  hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'],
261                  common_tools_var='VS80COMNTOOLS',
262                  executable_path=r'Common7\IDE\VCExpress.exe',
263                  batch_file_path=r'Common7\Tools\vsvars32.bat',
264                  default_dirname='Microsoft Visual Studio 8',
265                  supported_arch=['x86'],
266     ),
267
268     # Visual Studio .NET 2003
269     # The batch file we look for is in the Common7\Tools directory,
270     # so the devenv.com executable is next door in ..\IDE.
271     VisualStudio('7.1',
272                  sdk_version='6.0',
273                  hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'],
274                  common_tools_var='VS71COMNTOOLS',
275                  executable_path=r'Common7\IDE\devenv.com',
276                  batch_file_path=r'Common7\Tools\vsvars32.bat',
277                  default_dirname='Microsoft Visual Studio .NET 2003',
278                  supported_arch=['x86'],
279     ),
280
281     # Visual Studio .NET
282     # The batch file we look for is in the Common7\Tools directory,
283     # so the devenv.com executable is next door in ..\IDE.
284     VisualStudio('7.0',
285                  sdk_version='2003R2',
286                  hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'],
287                  common_tools_var='VS70COMNTOOLS',
288                  executable_path=r'IDE\devenv.com',
289                  batch_file_path=r'Common7\Tools\vsvars32.bat',
290                  default_dirname='Microsoft Visual Studio .NET',
291                  supported_arch=['x86'],
292     ),
293
294     # Visual Studio 6.0
295     VisualStudio('6.0',
296                  sdk_version='2003R1',
297                  hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir',
298                         'use_dir'],
299                  common_tools_var='VS60COMNTOOLS',
300                  executable_path=r'Common\MSDev98\Bin\MSDEV.COM',
301                  batch_file_path=r'Common7\Tools\vsvars32.bat',
302                  default_dirname='Microsoft Visual Studio',
303                  supported_arch=['x86'],
304     ),
305 ]
306
307 SupportedVSMap = {}
308 for vs in SupportedVSList:
309     SupportedVSMap[vs.version] = vs
310
311
312 # Finding installed versions of Visual Studio isn't cheap, because it
313 # goes not only to the registry but also to the disk to sanity-check
314 # that there is, in fact, a Visual Studio directory there and that the
315 # registry entry isn't just stale.  Find this information once, when
316 # requested, and cache it.
317
318 InstalledVSList = None
319 InstalledVSMap  = None
320
321 def get_installed_visual_studios():
322     global InstalledVSList
323     global InstalledVSMap
324     if InstalledVSList is None:
325         InstalledVSList = []
326         InstalledVSMap = {}
327         for vs in SupportedVSList:
328             debug('trying to find VS %s' % vs.version)
329             if vs.get_executable():
330                 debug('found VS %s' % vs.version)
331                 InstalledVSList.append(vs)
332                 InstalledVSMap[vs.version] = vs
333     return InstalledVSList
334
335 def reset_installed_visual_studios():
336     global InstalledVSList
337     global InstalledVSMap
338     InstalledVSList = None
339     InstalledVSMap  = None
340     for vs in SupportedVSList:
341         vs.reset()
342         
343     # Need to clear installed VC's as well as they are used in finding
344     # installed VS's
345     SCons.Tool.MSCommon.vc.reset_installed_vcs()
346         
347
348 # We may be asked to update multiple construction environments with
349 # SDK information.  When doing this, we check on-disk for whether
350 # the SDK has 'mfc' and 'atl' subdirectories.  Since going to disk
351 # is expensive, cache results by directory.
352
353 #SDKEnvironmentUpdates = {}
354 #
355 #def set_sdk_by_directory(env, sdk_dir):
356 #    global SDKEnvironmentUpdates
357 #    try:
358 #        env_tuple_list = SDKEnvironmentUpdates[sdk_dir]
359 #    except KeyError:
360 #        env_tuple_list = []
361 #        SDKEnvironmentUpdates[sdk_dir] = env_tuple_list
362 #
363 #        include_path = os.path.join(sdk_dir, 'include')
364 #        mfc_path = os.path.join(include_path, 'mfc')
365 #        atl_path = os.path.join(include_path, 'atl')
366 #
367 #        if os.path.exists(mfc_path):
368 #            env_tuple_list.append(('INCLUDE', mfc_path))
369 #        if os.path.exists(atl_path):
370 #            env_tuple_list.append(('INCLUDE', atl_path))
371 #        env_tuple_list.append(('INCLUDE', include_path))
372 #
373 #        env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib')))
374 #        env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib')))
375 #        env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin')))
376 #
377 #    for variable, directory in env_tuple_list:
378 #        env.PrependENVPath(variable, directory)
379
380 def msvs_exists():
381     return (len(get_installed_visual_studios()) > 0)
382
383 def get_vs_by_version(msvs):
384     global InstalledVSMap
385     global SupportedVSMap
386
387     debug('vs.py:get_vs_by_version()')
388     if msvs not in SupportedVSMap:
389         msg = "Visual Studio version %s is not supported" % repr(msvs)
390         raise SCons.Errors.UserError, msg
391     get_installed_visual_studios()
392     vs = InstalledVSMap.get(msvs)
393     debug('InstalledVSMap:%s'%InstalledVSMap)
394     debug('vs.py:get_vs_by_version: found vs:%s'%vs)
395     # Some check like this would let us provide a useful error message
396     # if they try to set a Visual Studio version that's not installed.
397     # However, we also want to be able to run tests (like the unit
398     # tests) on systems that don't, or won't ever, have it installed.
399     # It might be worth resurrecting this, with some configurable
400     # setting that the tests can use to bypass the check.
401     #if not vs:
402     #    msg = "Visual Studio version %s is not installed" % repr(msvs)
403     #    raise SCons.Errors.UserError, msg
404     return vs
405
406 def get_default_version(env):
407     """Returns the default version string to use for MSVS.
408
409     If no version was requested by the user through the MSVS environment
410     variable, query all the available the visual studios through
411     query_versions, and take the highest one.
412
413     Return
414     ------
415     version: str
416         the default version.
417     """
418     if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']):
419         # TODO(1.5):
420         #versions = [vs.version for vs in get_installed_visual_studios()]
421         versions = [vs.version for vs in get_installed_visual_studios()]
422         env['MSVS'] = {'VERSIONS' : versions}
423     else:
424         versions = env['MSVS'].get('VERSIONS', [])
425
426     if 'MSVS_VERSION' not in env:
427         if versions:
428             env['MSVS_VERSION'] = versions[0] #use highest version by default
429         else:
430             env['MSVS_VERSION'] = SupportedVSList[0].version
431
432     env['MSVS']['VERSION'] = env['MSVS_VERSION']
433
434     return env['MSVS_VERSION']
435
436 def get_default_arch(env):
437     """Return the default arch to use for MSVS
438
439     if no version was requested by the user through the MSVS_ARCH environment
440     variable, select x86
441
442     Return
443     ------
444     arch: str
445     """
446     arch = env.get('MSVS_ARCH', 'x86')
447
448     msvs = InstalledVSMap.get(env['MSVS_VERSION'])
449
450     if not msvs:
451         arch = 'x86'
452     elif not arch in msvs.get_supported_arch():
453         fmt = "Visual Studio version %s does not support architecture %s"
454         raise SCons.Errors.UserError, fmt % (env['MSVS_VERSION'], arch)
455
456     return arch
457
458 def merge_default_version(env):
459     version = get_default_version(env)
460     arch = get_default_arch(env)
461
462 def msvs_setup_env(env):
463     batfilename = msvs.get_batch_file()
464     msvs = get_vs_by_version(version)
465     if msvs is None:
466         return
467
468     # XXX: I think this is broken. This will silently set a bogus tool instead
469     # of failing, but there is no other way with the current scons tool
470     # framework
471     if batfilename is not None:
472
473         vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE')
474
475         msvs_list = get_installed_visual_studios()
476         # TODO(1.5):
477         #vscommonvarnames = [ vs.common_tools_var for vs in msvs_list ]
478         vscommonvarnames = [vs.common_tools_var for vs in msvs_list]
479         nenv = normalize_env(env['ENV'], vscommonvarnames + ['COMSPEC'])
480         output = get_output(batfilename, arch, env=nenv)
481         vars = parse_output(output, vars)
482
483         for k, v in vars.items():
484             env.PrependENVPath(k, v, delete_existing=1)
485
486 def query_versions():
487     """Query the system to get available versions of VS. A version is
488     considered when a batfile is found."""
489     msvs_list = get_installed_visual_studios()
490     # TODO(1.5)
491     #versions = [ msvs.version for msvs in msvs_list ]
492     versions = [msvs.version for msvs in msvs_list]
493     return versions
494
495 # Local Variables:
496 # tab-width:4
497 # indent-tabs-mode:nil
498 # End:
499 # vim: set expandtab tabstop=4 shiftwidth=4: