It should now handle picking up the SDK compilers when the VC vcvarsall.bat doesn...
[scons.git] / src / engine / SCons / Tool / MSCommon / sdk.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 the Platform/Windows SDK
27
28 PSDK 2003 R1 is the earliest version detected.
29 """
30
31 import os
32
33 import SCons.Errors
34 import SCons.Util
35
36 import common
37
38 debug = common.debug
39
40 # SDK Checks. This is of course a mess as everything else on MS platforms. Here
41 # is what we do to detect the SDK:
42 #
43 # For Windows SDK >= 6.0: just look into the registry entries:
44 #   HKLM\Software\Microsoft\Microsoft SDKs\Windows
45 # All the keys in there are the available versions.
46 #
47 # For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not
48 # seem to be any sane registry key, so the precise location is hardcoded.
49 #
50 # For versions below 2003R1, it seems the PSDK is included with Visual Studio?
51 #
52 # Also, per the following:
53 #     http://benjamin.smedbergs.us/blog/tag/atl/
54 # VC++ Professional comes with the SDK, VC++ Express does not.
55
56 # Location of the SDK (checked for 6.1 only)
57 _CURINSTALLED_SDK_HKEY_ROOT = \
58         r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder"
59
60
61 class SDKDefinition:
62     """
63     An abstract base class for trying to find installed SDK directories.
64     """
65     def __init__(self, version, **kw):
66         self.version = version
67         self.__dict__.update(kw)
68
69     def find_sdk_dir(self):
70         """Try to find the MS SDK from the registry.
71
72         Return None if failed or the directory does not exist.
73         """
74         if not SCons.Util.can_read_reg:
75             debug('find_sdk_dir(): can not read registry')
76             return None
77
78         hkey = self.HKEY_FMT % self.hkey_data
79
80         try:
81             sdk_dir = common.read_reg(hkey)
82         except WindowsError, e:
83             debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey))
84             return None
85
86         if not os.path.exists(sdk_dir):
87             debug('find_sdk_dir():  %s not on file system' % sdk_dir)
88             return None
89
90         ftc = os.path.join(sdk_dir, self.sanity_check_file)
91         if not os.path.exists(ftc):
92             debug("find_sdk_dir(): sanity check %s not found" % ftc)
93             return None
94
95         return sdk_dir
96
97     def get_sdk_dir(self):
98         """Return the MSSSDK given the version string."""
99         try:
100             return self._sdk_dir
101         except AttributeError:
102             sdk_dir = self.find_sdk_dir()
103             self._sdk_dir = sdk_dir
104             return sdk_dir
105         
106     def get_sdk_vc_script(self,host_arch, target_arch):
107         """ Return the script to initialize the VC compiler installed by SDK
108         """
109         
110         arch_string=target_arch
111         if (host_arch != target_arch):
112             arch_string='%s_%s'%(host_arch,target_arch)
113             
114         #print "arch_string:%s host_arch:%s target_arch:%s"%(arch_string,
115         #                                                   host_arch,
116         #                                                   target_arch)
117         file=self.vc_setup_scripts.get(arch_string,None)
118         #print "FILE:%s"%file
119         return file
120
121 class WindowsSDK(SDKDefinition):
122     """
123     A subclass for trying to find installed Windows SDK directories.
124     """
125     HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder'
126     def __init__(self, *args, **kw):
127         apply(SDKDefinition.__init__, (self,)+args, kw)
128         self.hkey_data = self.version
129
130 class PlatformSDK(SDKDefinition):
131     """
132     A subclass for trying to find installed Platform SDK directories.
133     """
134     HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir'
135     def __init__(self, *args, **kw):
136         apply(SDKDefinition.__init__, (self,)+args, kw)
137         self.hkey_data = self.uuid
138
139 #
140 # The list of VC initialization scripts installed by the SDK
141 # These should be tried if the vcvarsall.bat TARGET_ARCH fails
142 preSDK61VCSetupScripts = { 'x86'      : r'bin\vcvars32.bat',
143                            'amd64'    : r'bin\vcvarsamd64.bat',
144                            'x86_amd64': r'bin\vcvarsx86_amd64.bat',
145                            'x86_ia64' : r'bin\vcvarsx86_ia64.bat',
146                            'ia64'     : r'bin\vcvarsia64.bat'}
147
148 SDk61AndLaterVCSetupScripts = {'x86'      : r'bin\vcvars32.bat',
149                                'amd64'    : r'bin\amd64\vcvarsamd64.bat',
150                                'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat',
151                                'x86_ia64' : r'bin\x86_ia64\vcvarsx86_ia64.bat',
152                                'ia64'     : r'bin\ia64\vcvarsia64.bat'}
153
154 # The list of support SDKs which we know how to detect.
155 #
156 # The first SDK found in the list is the one used by default if there
157 # are multiple SDKs installed.  Barring good reasons to the contrary,
158 # this means we should list SDKs with from most recent to oldest.
159 #
160 # If you update this list, update the documentation in Tool/mssdk.xml.
161 SupportedSDKList = [
162     WindowsSDK('7.0',
163                sanity_check_file=r'bin\SetEnv.Cmd',
164                include_subdir='include',
165                lib_subdir={
166                    'x86'       : ['lib'],
167                    'x86_64'    : [r'lib\x64'],
168                    'ia64'      : [r'lib\ia64'],
169                },
170                vc_setup_scripts = SDk61AndLaterVCSetupScripts,
171               ),
172     WindowsSDK('6.1',
173                sanity_check_file=r'bin\SetEnv.Cmd',
174                include_subdir='include',
175                lib_subdir={
176                    'x86'       : ['lib'],
177                    'x86_64'    : [r'lib\x64'],
178                    'ia64'      : [r'lib\ia64'],
179                },
180                vc_setup_scripts = SDk61AndLaterVCSetupScripts,
181               ),
182
183     WindowsSDK('6.0A',
184                sanity_check_file=r'include\windows.h',
185                include_subdir='include',
186                lib_subdir={
187                    'x86'       : ['lib'],
188                    'x86_64'    : [r'lib\x64'],
189                    'ia64'      : [r'lib\ia64'],
190                },
191                vc_setup_scripts = preSDK61VCSetupScripts,
192               ),
193
194     WindowsSDK('6.0',
195                sanity_check_file=r'bin\gacutil.exe',
196                include_subdir='include',
197                lib_subdir='lib',
198                vc_setup_scripts = preSDK61VCSetupScripts,
199               ),
200
201     PlatformSDK('2003R2',
202                 sanity_check_file=r'SetEnv.Cmd',
203                 uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1",
204                 vc_setup_scripts = preSDK61VCSetupScripts,
205                ),
206
207     PlatformSDK('2003R1',
208                 sanity_check_file=r'SetEnv.Cmd',
209                 uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3",
210                 vc_setup_scripts = preSDK61VCSetupScripts,
211                ),
212 ]
213
214 SupportedSDKMap = {}
215 for sdk in SupportedSDKList:
216     SupportedSDKMap[sdk.version] = sdk
217
218
219 # Finding installed SDKs isn't cheap, because it goes not only to the
220 # registry but also to the disk to sanity-check that there is, in fact,
221 # an SDK installed there and that the registry entry isn't just stale.
222 # Find this information once, when requested, and cache it.
223
224 InstalledSDKList = None
225 InstalledSDKMap = None
226
227 def get_installed_sdks():
228     global InstalledSDKList
229     global InstalledSDKMap
230     if InstalledSDKList is None:
231         InstalledSDKList = []
232         InstalledSDKMap = {}
233         for sdk in SupportedSDKList:
234             debug('trying to find SDK %s' % sdk.version)
235             if sdk.get_sdk_dir():
236                 debug('found SDK %s' % sdk.version)
237                 InstalledSDKList.append(sdk)
238                 InstalledSDKMap[sdk.version] = sdk
239     return InstalledSDKList
240
241
242 # We may be asked to update multiple construction environments with
243 # SDK information.  When doing this, we check on-disk for whether
244 # the SDK has 'mfc' and 'atl' subdirectories.  Since going to disk
245 # is expensive, cache results by directory.
246
247 SDKEnvironmentUpdates = {}
248
249 def set_sdk_by_directory(env, sdk_dir):
250     global SDKEnvironmentUpdates
251     debug('set_sdk_by_directory: Using dir:%s'%sdk_dir)
252     try:
253         env_tuple_list = SDKEnvironmentUpdates[sdk_dir]
254     except KeyError:
255         env_tuple_list = []
256         SDKEnvironmentUpdates[sdk_dir] = env_tuple_list
257
258         include_path = os.path.join(sdk_dir, 'include')
259         mfc_path = os.path.join(include_path, 'mfc')
260         atl_path = os.path.join(include_path, 'atl')
261
262         if os.path.exists(mfc_path):
263             env_tuple_list.append(('INCLUDE', mfc_path))
264         if os.path.exists(atl_path):
265             env_tuple_list.append(('INCLUDE', atl_path))
266         env_tuple_list.append(('INCLUDE', include_path))
267
268         env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib')))
269         env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib')))
270         env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin')))
271
272     for variable, directory in env_tuple_list:
273         env.PrependENVPath(variable, directory)
274
275
276 # TODO(sgk):  currently unused; remove?
277 def get_cur_sdk_dir_from_reg():
278     """Try to find the platform sdk directory from the registry.
279
280     Return None if failed or the directory does not exist"""
281     if not SCons.Util.can_read_reg:
282         debug('SCons cannot read registry')
283         return None
284
285     try:
286         val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT)
287         debug("Found current sdk dir in registry: %s" % val)
288     except WindowsError, e:
289         debug("Did not find current sdk in registry")
290         return None
291
292     if not os.path.exists(val):
293         debug("Current sdk dir %s not on fs" % val)
294         return None
295
296     return val
297
298 def get_sdk_by_version(mssdk):
299     if not SupportedSDKMap.has_key(mssdk):
300         msg = "SDK version %s is not supported" % repr(mssdk)
301         raise SCons.Errors.UserError, msg
302     get_installed_sdks()
303     return InstalledSDKMap.get(mssdk)
304
305 def get_default_sdk():
306     """Set up the default Platform/Windows SDK."""
307     get_installed_sdks()
308     if not InstalledSDKList:
309         return None
310     return InstalledSDKList[0]
311
312 def mssdk_setup_env(env):
313     debug('mssdk_setup_env()')
314     if env.has_key('MSSDK_DIR'):
315         sdk_dir = env['MSSDK_DIR']
316         if sdk_dir is None:
317             return
318         sdk_dir = env.subst(sdk_dir)
319     elif env.has_key('MSSDK_VERSION'):
320         sdk_version = env['MSSDK_VERSION']
321         if sdk_version is None:
322             msg = "SDK version %s is not installed" % repr(mssdk)
323             raise SCons.Errors.UserError, msg
324         sdk_version = env.subst(sdk_version)
325         mssdk = get_sdk_by_version(sdk_version)
326         sdk_dir = mssdk.get_sdk_dir()
327     elif env.has_key('MSVS_VERSION'):
328         msvs_version = env['MSVS_VERSION']
329         debug('Getting MSVS_VERSION from env:%s'%msvs_version)
330         if msvs_version is None:
331             return
332         msvs_version = env.subst(msvs_version)
333         import vs
334         msvs = vs.get_vs_by_version(msvs_version)
335         debug('msvs is :%s'%msvs)
336         if not msvs:
337             return
338         sdk_version = msvs.sdk_version
339         if not sdk_version:
340             return
341         mssdk = get_sdk_by_version(sdk_version)
342         if not mssdk:
343             mssdk = get_default_sdk()
344             if not mssdk:
345                 return
346         sdk_dir = mssdk.get_sdk_dir()
347     else:
348         mssdk = get_default_sdk()
349         if not mssdk:
350             return
351         sdk_dir = mssdk.get_sdk_dir()
352
353     set_sdk_by_directory(env, sdk_dir)
354
355     #print "No MSVS_VERSION: this is likely to be a bug"
356
357 def mssdk_exists(version=None):
358     sdks = get_installed_sdks()
359     if version is None:
360         return len(sdks) > 0
361     return sdks.has_key(version)
362
363 # Local Variables:
364 # tab-width:4
365 # indent-tabs-mode:nil
366 # End:
367 # vim: set expandtab tabstop=4 shiftwidth=4: