http://scons.tigris.org/issues/show_bug.cgi?id=2345
[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         debug('find_sdk_dir(): checking registry:%s'%hkey)
80
81         try:
82             sdk_dir = common.read_reg(hkey)
83         except WindowsError, e:
84             debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey))
85             return None
86
87         debug('find_sdk_dir(): Trying SDK Dir: %s'%sdk_dir)
88
89         if not os.path.exists(sdk_dir):
90             debug('find_sdk_dir():  %s not on file system' % sdk_dir)
91             return None
92
93         ftc = os.path.join(sdk_dir, self.sanity_check_file)
94         if not os.path.exists(ftc):
95             debug("find_sdk_dir(): sanity check %s not found" % ftc)
96             return None
97
98         return sdk_dir
99
100     def get_sdk_dir(self):
101         """Return the MSSSDK given the version string."""
102         try:
103             return self._sdk_dir
104         except AttributeError:
105             sdk_dir = self.find_sdk_dir()
106             self._sdk_dir = sdk_dir
107             return sdk_dir
108         
109     def get_sdk_vc_script(self,host_arch, target_arch):
110         """ Return the script to initialize the VC compiler installed by SDK
111         """
112
113         if (host_arch == 'amd64' and target_arch == 'x86'):
114             # No cross tools needed compiling 32 bits on 64 bit machine
115             host_arch=target_arch
116         
117         arch_string=target_arch
118         if (host_arch != target_arch):
119             arch_string='%s_%s'%(host_arch,target_arch)
120             
121         debug("sdk.py: get_sdk_vc_script():arch_string:%s host_arch:%s target_arch:%s"%(arch_string,
122                                                            host_arch,
123                                                            target_arch))
124         file=self.vc_setup_scripts.get(arch_string,None)
125         debug("sdk.py: get_sdk_vc_script():file:%s"%file)
126         return file
127
128 class WindowsSDK(SDKDefinition):
129     """
130     A subclass for trying to find installed Windows SDK directories.
131     """
132     HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder'
133     def __init__(self, *args, **kw):
134         SDKDefinition.__init__(self, *args, **kw)
135         self.hkey_data = self.version
136
137 class PlatformSDK(SDKDefinition):
138     """
139     A subclass for trying to find installed Platform SDK directories.
140     """
141     HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir'
142     def __init__(self, *args, **kw):
143         SDKDefinition.__init__(self, *args, **kw)
144         self.hkey_data = self.uuid
145
146 #
147 # The list of VC initialization scripts installed by the SDK
148 # These should be tried if the vcvarsall.bat TARGET_ARCH fails
149 preSDK61VCSetupScripts = { 'x86'      : r'bin\vcvars32.bat',
150                            'amd64'    : r'bin\vcvarsamd64.bat',
151                            'x86_amd64': r'bin\vcvarsx86_amd64.bat',
152                            'x86_ia64' : r'bin\vcvarsx86_ia64.bat',
153                            'ia64'     : r'bin\vcvarsia64.bat'}
154
155 SDK61VCSetupScripts = {'x86'      : r'bin\vcvars32.bat',
156                        'amd64'    : r'bin\amd64\vcvarsamd64.bat',
157                        'x86_amd64': r'bin\x86_amd64\vcvarsx86_amd64.bat',
158                        'x86_ia64' : r'bin\x86_ia64\vcvarsx86_ia64.bat',
159                        'ia64'     : r'bin\ia64\vcvarsia64.bat'}
160
161 SDK70VCSetupScripts =    { 'x86'      : r'bin\vcvars32.bat',
162                            'amd64'    : r'bin\vcvars64.bat',
163                            'x86_amd64': r'bin\vcvarsx86_amd64.bat',
164                            'x86_ia64' : r'bin\vcvarsx86_ia64.bat',
165                            'ia64'     : r'bin\vcvarsia64.bat'}
166
167 # The list of support SDKs which we know how to detect.
168 #
169 # The first SDK found in the list is the one used by default if there
170 # are multiple SDKs installed.  Barring good reasons to the contrary,
171 # this means we should list SDKs with from most recent to oldest.
172 #
173 # If you update this list, update the documentation in Tool/mssdk.xml.
174 SupportedSDKList = [
175     WindowsSDK('7.0',
176                sanity_check_file=r'bin\SetEnv.Cmd',
177                include_subdir='include',
178                lib_subdir={
179                    'x86'       : ['lib'],
180                    'x86_64'    : [r'lib\x64'],
181                    'ia64'      : [r'lib\ia64'],
182                },
183                vc_setup_scripts = SDK70VCSetupScripts,
184               ),
185     WindowsSDK('6.1',
186                sanity_check_file=r'bin\SetEnv.Cmd',
187                include_subdir='include',
188                lib_subdir={
189                    'x86'       : ['lib'],
190                    'x86_64'    : [r'lib\x64'],
191                    'ia64'      : [r'lib\ia64'],
192                },
193                vc_setup_scripts = SDK61VCSetupScripts,
194               ),
195
196     WindowsSDK('6.0A',
197                sanity_check_file=r'include\windows.h',
198                include_subdir='include',
199                lib_subdir={
200                    'x86'       : ['lib'],
201                    'x86_64'    : [r'lib\x64'],
202                    'ia64'      : [r'lib\ia64'],
203                },
204                vc_setup_scripts = preSDK61VCSetupScripts,
205               ),
206
207     WindowsSDK('6.0',
208                sanity_check_file=r'bin\gacutil.exe',
209                include_subdir='include',
210                lib_subdir='lib',
211                vc_setup_scripts = preSDK61VCSetupScripts,
212               ),
213
214     PlatformSDK('2003R2',
215                 sanity_check_file=r'SetEnv.Cmd',
216                 uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1",
217                 vc_setup_scripts = preSDK61VCSetupScripts,
218                ),
219
220     PlatformSDK('2003R1',
221                 sanity_check_file=r'SetEnv.Cmd',
222                 uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3",
223                 vc_setup_scripts = preSDK61VCSetupScripts,
224                ),
225 ]
226
227 SupportedSDKMap = {}
228 for sdk in SupportedSDKList:
229     SupportedSDKMap[sdk.version] = sdk
230
231
232 # Finding installed SDKs isn't cheap, because it goes not only to the
233 # registry but also to the disk to sanity-check that there is, in fact,
234 # an SDK installed there and that the registry entry isn't just stale.
235 # Find this information once, when requested, and cache it.
236
237 InstalledSDKList = None
238 InstalledSDKMap = None
239
240 def get_installed_sdks():
241     global InstalledSDKList
242     global InstalledSDKMap
243     debug('sdk.py:get_installed_sdks()')
244     if InstalledSDKList is None:
245         InstalledSDKList = []
246         InstalledSDKMap = {}
247         for sdk in SupportedSDKList:
248             debug('MSCommon/sdk.py: trying to find SDK %s' % sdk.version)
249             if sdk.get_sdk_dir():
250                 debug('MSCommon/sdk.py:found SDK %s' % sdk.version)
251                 InstalledSDKList.append(sdk)
252                 InstalledSDKMap[sdk.version] = sdk
253     return InstalledSDKList
254
255
256 # We may be asked to update multiple construction environments with
257 # SDK information.  When doing this, we check on-disk for whether
258 # the SDK has 'mfc' and 'atl' subdirectories.  Since going to disk
259 # is expensive, cache results by directory.
260
261 SDKEnvironmentUpdates = {}
262
263 def set_sdk_by_directory(env, sdk_dir):
264     global SDKEnvironmentUpdates
265     debug('set_sdk_by_directory: Using dir:%s'%sdk_dir)
266     try:
267         env_tuple_list = SDKEnvironmentUpdates[sdk_dir]
268     except KeyError:
269         env_tuple_list = []
270         SDKEnvironmentUpdates[sdk_dir] = env_tuple_list
271
272         include_path = os.path.join(sdk_dir, 'include')
273         mfc_path = os.path.join(include_path, 'mfc')
274         atl_path = os.path.join(include_path, 'atl')
275
276         if os.path.exists(mfc_path):
277             env_tuple_list.append(('INCLUDE', mfc_path))
278         if os.path.exists(atl_path):
279             env_tuple_list.append(('INCLUDE', atl_path))
280         env_tuple_list.append(('INCLUDE', include_path))
281
282         env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib')))
283         env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib')))
284         env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin')))
285
286     for variable, directory in env_tuple_list:
287         env.PrependENVPath(variable, directory)
288
289
290 # TODO(sgk):  currently unused; remove?
291 def get_cur_sdk_dir_from_reg():
292     """Try to find the platform sdk directory from the registry.
293
294     Return None if failed or the directory does not exist"""
295     if not SCons.Util.can_read_reg:
296         debug('SCons cannot read registry')
297         return None
298
299     try:
300         val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT)
301         debug("Found current sdk dir in registry: %s" % val)
302     except WindowsError, e:
303         debug("Did not find current sdk in registry")
304         return None
305
306     if not os.path.exists(val):
307         debug("Current sdk dir %s not on fs" % val)
308         return None
309
310     return val
311
312 def get_sdk_by_version(mssdk):
313     if mssdk not in SupportedSDKMap:
314         msg = "SDK version %s is not supported" % repr(mssdk)
315         raise SCons.Errors.UserError(msg)
316     get_installed_sdks()
317     return InstalledSDKMap.get(mssdk)
318
319 def get_default_sdk():
320     """Set up the default Platform/Windows SDK."""
321     get_installed_sdks()
322     if not InstalledSDKList:
323         return None
324     return InstalledSDKList[0]
325
326
327
328
329 def mssdk_setup_env(env):
330     debug('sdk.py:mssdk_setup_env()')
331     if 'MSSDK_DIR' in env:
332         sdk_dir = env['MSSDK_DIR']
333         if sdk_dir is None:
334             return
335         sdk_dir = env.subst(sdk_dir)
336         debug('sdk.py:mssdk_setup_env: Using MSSDK_DIR:%s'%sdk_dir)
337     elif 'MSSDK_VERSION' in env:
338         sdk_version = env['MSSDK_VERSION']
339         if sdk_version is None:
340             msg = "SDK version %s is not installed" % repr(mssdk)
341             raise SCons.Errors.UserError(msg)
342         sdk_version = env.subst(sdk_version)
343         mssdk = get_sdk_by_version(sdk_version)
344         sdk_dir = mssdk.get_sdk_dir()
345         debug('sdk.py:mssdk_setup_env: Using MSSDK_VERSION:%s'%sdk_dir)
346     elif 'MSVS_VERSION' in env:
347         msvs_version = env['MSVS_VERSION']
348         debug('sdk.py:mssdk_setup_env:Getting MSVS_VERSION from env:%s'%msvs_version)
349         if msvs_version is None:
350             debug('sdk.py:mssdk_setup_env thinks msvs_version is None')
351             return
352         msvs_version = env.subst(msvs_version)
353         import vs
354         msvs = vs.get_vs_by_version(msvs_version)
355         debug('sdk.py:mssdk_setup_env:msvs is :%s'%msvs)
356         if not msvs:
357             debug('sdk.py:mssdk_setup_env: no VS version detected, bailingout:%s'%msvs)
358             return
359         sdk_version = msvs.sdk_version
360         debug('sdk.py:msvs.sdk_version is %s'%sdk_version)
361         if not sdk_version:
362             return
363         mssdk = get_sdk_by_version(sdk_version)
364         if not mssdk:
365             mssdk = get_default_sdk()
366             if not mssdk:
367                 return
368         sdk_dir = mssdk.get_sdk_dir()
369         debug('sdk.py:mssdk_setup_env: Using MSVS_VERSION:%s'%sdk_dir)
370     else:
371         mssdk = get_default_sdk()
372         if not mssdk:
373             return
374         sdk_dir = mssdk.get_sdk_dir()
375         debug('sdk.py:mssdk_setup_env: not using any env values. sdk_dir:%s'%sdk_dir)
376
377     set_sdk_by_directory(env, sdk_dir)
378
379     #print "No MSVS_VERSION: this is likely to be a bug"
380
381 def mssdk_exists(version=None):
382     sdks = get_installed_sdks()
383     if version is None:
384         return len(sdks) > 0
385     return version in sdks
386
387 # Local Variables:
388 # tab-width:4
389 # indent-tabs-mode:nil
390 # End:
391 # vim: set expandtab tabstop=4 shiftwidth=4: