REF: move vc2 to vc module.
[scons.git] / src / engine / SCons / Tool / MSCommon / vc.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 # TODO:
25 #   * supported arch for versions: for old versions of batch file without
26 #     argument, giving bogus argument cannot be detected, so we have to hardcode
27 #     this here
28 #   * print warning when msvc version specified but not found
29 #   * find out why warning do not print
30 #   * test on 64 bits XP +  VS 2005 (and VS 6 if possible)
31 #   * SDK
32 #   * Assembly
33 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
34
35 __doc__ = """Module for Visual C/C++ detection and configuration.
36 """
37
38 import os
39 import platform
40
41 import SCons.Warnings
42
43 import common
44
45 debug = common.debug
46
47 class BatchFileExecutionError(Exception):
48     pass
49
50 # Dict to 'canonalize' the arch
51 _ARCH_TO_CANONICAL = {
52     "x86": "x86",
53     "amd64": "amd64",
54     "i386": "x86",
55     "emt64": "amd64",
56     "x86_64": "amd64"
57 }
58
59 # Given a (host, target) tuple, return the argument for the bat file. Both host
60 # and targets should be canonalized.
61 _HOST_TARGET_ARCH_TO_BAT_ARCH = {
62     ("x86", "x86"): "x86",
63     ("x86", "amd64"): "x86_amd64",
64     ("amd64", "amd64"): "amd64",
65     ("amd64", "x86"): "x86"
66 }
67
68 def get_host_target(env):
69     host_platform = env.get('HOST_ARCH')
70     if not host_platform:
71         host_platform = platform.machine()
72     target_platform = env.get('TARGET_ARCH')
73     if not target_platform:
74         target_platform = host_platform
75
76     return (_ARCH_TO_CANONICAL[host_platform], 
77             _ARCH_TO_CANONICAL[target_platform])
78
79 _VCVER = ["10.0", "9.0", "8.0", "7.1", "7.0", "6.0"]
80
81 _VCVER_TO_PRODUCT_DIR = {
82         '10.0': [
83             r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'],
84         '9.0': [
85             r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',
86             r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'],
87         '8.0': [
88             r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir',
89             r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'],
90         '7.1': [
91             r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'],
92         '7.0': [
93             r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'],
94         '6.0': [
95             r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir']
96 }
97
98 def find_vc_pdir(msvc_version):
99     """Try to find the product directory for the given
100     version."""
101     root = 'Software\\'
102     if common.is_win64():
103         root = root + 'Wow6432Node\\'
104     try:
105         hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version]
106     except KeyError:
107         debug("Unknown version of MSVC: %s" % msvc_version)
108         return None
109
110     for key in hkeys:
111         key = root + key
112         try:
113             comps = common.read_reg(key)
114         except WindowsError, e:
115             debug('find_vc_dir(): no VC registry key %s' % repr(key))
116         else:
117             debug('find_vc_dir(): found VC in registry: %s' % comps)
118             if os.path.exists(comps):
119                 return comps
120             else:
121                 debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\
122                           % comps)
123                 return None
124     return None
125
126 def find_batch_file(msvc_version):
127     pdir = find_vc_pdir(msvc_version)
128     if pdir is None:
129         return None
130
131     vernum = float(msvc_version)
132     if 7 <= vernum < 8:
133         pdir = os.path.join(pdir, os.pardir, "Common7", "Tools")
134         batfilename = os.path.join(pdir, "vsvars32.bat")
135     elif vernum < 7:
136         pdir = os.path.join(pdir, "Bin")
137         batfilename = os.path.join(pdir, "vcvars32.bat")
138     else: # >= 8
139         batfilename = os.path.join(pdir, "vcvarsall.bat")
140
141     if os.path.exists(batfilename):
142         return batfilename
143     else:
144         debug("Not found: %s" % batfilename)
145         return None
146
147
148 def get_installed_vcs():
149     installed_versions = []
150     for ver in _VCVER:
151         debug('trying to find VC %s' % ver)
152         if find_vc_pdir(ver):
153             debug('found VC %s' % ver)
154             installed_versions.append(ver)
155     return installed_versions
156
157 def script_env(script, args=None):
158     stdout = common.get_output(script, args)
159     # Stupid batch files do not set return code: we take a look at the
160     # beginning of the output for an error message instead
161     olines = stdout.splitlines()
162     if olines[0].startswith("The specified configuration type is missing"):
163         raise BatchFileExecutionError("\n".join(olines[:2]))
164
165     return common.parse_output(stdout)
166
167 def get_default_version(env):
168     debug('get_default_version()')
169
170     msvc_version = env.get('MSVC_VERSION')
171     if not msvc_version:
172         installed_vcs = get_installed_vcs()
173         debug('installed_vcs:%s' % installed_vcs)
174         if not installed_vcs:
175             msg = 'No installed VCs'
176             debug('msv %s\n' % repr(msg))
177             SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
178             return None
179         msvc_version = installed_vcs[0]
180         debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version))
181
182     return msvc_version
183
184 def msvc_setup_env_once(env):
185     try:
186         has_run  = env["MSVC_SETUP_RUN"]
187     except KeyError:
188         has_run = False
189
190     if not has_run:
191         msvc_setup_env(env)
192         env["MSVC_SETUP_RUN"] = False
193
194 def msvc_setup_env(env):
195     debug('msvc_setup_env()')
196
197     version = get_default_version(env)
198     host_platform, target_platform = get_host_target(env)
199     debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version))
200     env['MSVC_VERSION'] = version
201
202     script = find_batch_file(version)
203     if not script:
204         msg = 'VC version %s not installed' % version
205         debug('msv %s\n' % repr(msg))
206         SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
207         return None
208     print script
209
210
211     use_script = env.get('MSVC_USE_SCRIPT', True)
212     if SCons.Util.is_String(use_script):
213         debug('use_script 1 %s\n' % repr(use_script))
214         d = script_env(use_script)
215     elif use_script:
216         host_target = (host_platform, target_platform)
217         arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target]
218         debug('use_script 2 %s, args:%s\n' % (repr(script), arg))
219         try:
220             d = script_env(script, args=arg)
221         except BatchFileExecutionError, e:
222             # XXX: find out why warnings do not work here
223             print "+++++++++++++++++++++++++++++"
224             msg = "Error while executing %s with args %s (error was %s)" % \
225                   (script, arg, str(e))
226             print msg
227             print "+++++++++++++++++++++++++++++"
228             return None
229     else:
230         debug('msvc.get_default_env()\n')
231         d = msvc.get_default_env()
232
233     for k, v in d.items():
234         env.PrependENVPath(k, v, delete_existing=True)
235
236 def msvc_setup_env_once(env):
237     try:
238         has_run  = env["MSVC_SETUP_RUN"]
239     except KeyError:
240         has_run = False
241
242     if not has_run:
243         msvc_setup_env(env)
244         env["MSVC_SETUP_RUN"] = True
245
246 def msvc_exists(version=None):
247     vcs = get_installed_vcs()
248     if version is None:
249         return len(vcs) > 0
250     return version in vcs
251