a50a6ae1e6b9498fc6d2fb06d68bd570efc06d5b
[scons.git] / src / engine / SCons / Platform / win32.py
1 """SCons.Platform.win32
2
3 Platform-specific initialization for Win32 systems.
4
5 There normally shouldn't be any need to import this module directly.  It
6 will usually be imported through the generic SCons.Platform.Platform()
7 selection method.
8 """
9
10 #
11 # __COPYRIGHT__
12 #
13 # Permission is hereby granted, free of charge, to any person obtaining
14 # a copy of this software and associated documentation files (the
15 # "Software"), to deal in the Software without restriction, including
16 # without limitation the rights to use, copy, modify, merge, publish,
17 # distribute, sublicense, and/or sell copies of the Software, and to
18 # permit persons to whom the Software is furnished to do so, subject to
19 # the following conditions:
20 #
21 # The above copyright notice and this permission notice shall be included
22 # in all copies or substantial portions of the Software.
23 #
24 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
25 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
26 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 #
32
33 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
34
35 import os
36 import os.path
37 import string
38 import sys
39 import tempfile
40
41 from SCons.Platform.posix import exitvalmap
42 from SCons.Platform import TempFileMunge
43 import SCons.Util
44
45 try:
46     import msvcrt
47     import win32api
48     import win32con
49
50     msvcrt.get_osfhandle
51     win32api.SetHandleInformation
52     win32con.HANDLE_FLAG_INHERIT
53 except ImportError:
54     parallel_msg = \
55         "you do not seem to have the pywin32 extensions installed;\n" + \
56         "\tparallel (-j) builds may not work reliably with open Python files."
57 except AttributeError:
58     parallel_msg = \
59         "your pywin32 extensions do not support file handle operations;\n" + \
60         "\tparallel (-j) builds may not work reliably with open Python files."
61 else:
62     parallel_msg = None
63
64     import __builtin__
65
66     _builtin_file = __builtin__.file
67     _builtin_open = __builtin__.open
68     
69     def _scons_file(*args, **kw):
70         fp = apply(_builtin_file, args, kw)
71         win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()),
72                                       win32con.HANDLE_FLAG_INHERIT,
73                                       0)
74         return fp
75
76     def _scons_open(*args, **kw):
77         fp = apply(_builtin_open, args, kw)
78         win32api.SetHandleInformation(msvcrt.get_osfhandle(fp.fileno()),
79                                       win32con.HANDLE_FLAG_INHERIT,
80                                       0)
81         return fp
82
83     __builtin__.file = _scons_file
84     __builtin__.open = _scons_open
85
86
87
88 # The upshot of all this is that, if you are using Python 1.5.2,
89 # you had better have cmd or command.com in your PATH when you run
90 # scons.
91
92 def piped_spawn(sh, escape, cmd, args, env, stdout, stderr):
93     # There is no direct way to do that in python. What we do
94     # here should work for most cases:
95     #   In case stdout (stderr) is not redirected to a file,
96     #   we redirect it into a temporary file tmpFileStdout
97     #   (tmpFileStderr) and copy the contents of this file
98     #   to stdout (stderr) given in the argument
99     if not sh:
100         sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
101         return 127
102     else:
103         # one temporary file for stdout and stderr
104         tmpFileStdout = os.path.normpath(tempfile.mktemp())
105         tmpFileStderr = os.path.normpath(tempfile.mktemp())
106
107         # check if output is redirected
108         stdoutRedirected = 0
109         stderrRedirected = 0
110         for arg in args:
111             # are there more possibilities to redirect stdout ?
112             if (string.find( arg, ">", 0, 1 ) != -1 or
113                 string.find( arg, "1>", 0, 2 ) != -1):
114                 stdoutRedirected = 1
115             # are there more possibilities to redirect stderr ?
116             if string.find( arg, "2>", 0, 2 ) != -1:
117                 stderrRedirected = 1
118
119         # redirect output of non-redirected streams to our tempfiles
120         if stdoutRedirected == 0:
121             args.append(">" + str(tmpFileStdout))
122         if stderrRedirected == 0:
123             args.append("2>" + str(tmpFileStderr))
124
125         # actually do the spawn
126         try:
127             args = [sh, '/C', escape(string.join(args)) ]
128             ret = os.spawnve(os.P_WAIT, sh, args, env)
129         except OSError, e:
130             # catch any error
131             try:
132                 ret = exitvalmap[e[0]]
133             except KeyError:
134                 sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1]))
135             if stderr is not None:
136                 stderr.write("scons: %s: %s\n" % (cmd, e[1]))
137         # copy child output from tempfiles to our streams
138         # and do clean up stuff
139         if stdout is not None and stdoutRedirected == 0:
140             try:
141                 stdout.write(open( tmpFileStdout, "r" ).read())
142                 os.remove( tmpFileStdout )
143             except (IOError, OSError):
144                 pass
145
146         if stderr is not None and stderrRedirected == 0:
147             try:
148                 stderr.write(open( tmpFileStderr, "r" ).read())
149                 os.remove( tmpFileStderr )
150             except (IOError, OSError):
151                 pass
152         return ret
153
154 def exec_spawn(l, env):
155     try:
156         result = os.spawnve(os.P_WAIT, l[0], l, env)
157     except OSError, e:
158         try:
159             result = exitvalmap[e[0]]
160             sys.stderr.write("scons: %s: %s\n" % (l[0], e[1]))
161         except KeyError:
162             result = 127
163             if len(l) > 2:
164                 if len(l[2]) < 1000:
165                     command = string.join(l[0:3])
166                 else:
167                     command = l[0]
168             else:
169                 command = l[0]
170             sys.stderr.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e[0], command, e[1]))
171     return result
172
173 def spawn(sh, escape, cmd, args, env):
174     if not sh:
175         sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
176         return 127
177     return exec_spawn([sh, '/C', escape(string.join(args))], env)
178
179 # Windows does not allow special characters in file names anyway, so no
180 # need for a complex escape function, we will just quote the arg, except
181 # that "cmd /c" requires that if an argument ends with a backslash it
182 # needs to be escaped so as not to interfere with closing double quote
183 # that we add.
184 def escape(x):
185     if x[-1] == '\\':
186         x = x + '\\'
187     return '"' + x + '"'
188
189 # Get the windows system directory name
190 _system_root = None
191
192 def get_system_root():
193     global _system_root
194     if _system_root is not None:
195         return _system_root
196
197     # A resonable default if we can't read the registry
198     val = os.environ.get('SystemRoot', "C:\\WINDOWS")
199
200     if SCons.Util.can_read_reg:
201         try:
202             # Look for Windows NT system root
203             k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
204                                       'Software\\Microsoft\\Windows NT\\CurrentVersion')
205             val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
206         except SCons.Util.RegError:
207             try:
208                 # Okay, try the Windows 9x system root
209                 k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
210                                           'Software\\Microsoft\\Windows\\CurrentVersion')
211                 val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
212             except KeyboardInterrupt:
213                 raise
214             except:
215                 pass
216     _system_root = val
217     return val
218
219 # Get the location of the program files directory
220 def get_program_files_dir():
221     # Now see if we can look in the registry...
222     val = ''
223     if SCons.Util.can_read_reg:
224         try:
225             # Look for Windows Program Files directory
226             k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
227                                       'Software\\Microsoft\\Windows\\CurrentVersion')
228             val, tok = SCons.Util.RegQueryValueEx(k, 'ProgramFilesDir')
229         except SCons.Util.RegError:
230             val = ''
231             pass
232
233     if val == '':
234         # A reasonable default if we can't read the registry
235         # (Actually, it's pretty reasonable even if we can :-)
236         val = os.path.join(os.path.dirname(get_system_root()),"Program Files")
237         
238     return val
239
240
241
242 # Determine which windows CPU were running on.
243 class ArchDefinition:
244     """
245     A class for defining architecture-specific settings and logic.
246     """
247     def __init__(self, arch, synonyms=[]):
248         self.arch = arch
249         self.synonyms = synonyms
250
251 SupportedArchitectureList = [
252     ArchDefinition(
253         'x86',
254         ['i386', 'i486', 'i586', 'i686'],
255     ),
256
257     ArchDefinition(
258         'x86_64',
259         ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'],
260     ),
261
262     ArchDefinition(
263         'ia64',
264         ['IA64'],
265     ),
266 ]
267
268 SupportedArchitectureMap = {}
269 for a in SupportedArchitectureList:
270     SupportedArchitectureMap[a.arch] = a
271     for s in a.synonyms:
272         SupportedArchitectureMap[s] = a
273
274 def get_architecture(arch=None):
275     """Returns the definition for the specified architecture string.
276
277     If no string is specified, the system default is returned (as defined
278     by the PROCESSOR_ARCHITEW6432 or PROCESSOR_ARCHITECTURE environment
279     variables).
280     """
281     if arch is None:
282         arch = os.environ.get('PROCESSOR_ARCHITEW6432')
283         if not arch:
284             arch = os.environ.get('PROCESSOR_ARCHITECTURE')
285     return SupportedArchitectureMap.get(arch, ArchDefinition('', ['']))
286
287 def generate(env):
288     # Attempt to find cmd.exe (for WinNT/2k/XP) or
289     # command.com for Win9x
290     cmd_interp = ''
291     # First see if we can look in the registry...
292     if SCons.Util.can_read_reg:
293         try:
294             # Look for Windows NT system root
295             k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
296                                           'Software\\Microsoft\\Windows NT\\CurrentVersion')
297             val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
298             cmd_interp = os.path.join(val, 'System32\\cmd.exe')
299         except SCons.Util.RegError:
300             try:
301                 # Okay, try the Windows 9x system root
302                 k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
303                                               'Software\\Microsoft\\Windows\\CurrentVersion')
304                 val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
305                 cmd_interp = os.path.join(val, 'command.com')
306             except KeyboardInterrupt:
307                 raise
308             except:
309                 pass
310
311     # For the special case of not having access to the registry, we
312     # use a temporary path and pathext to attempt to find the command
313     # interpreter.  If we fail, we try to find the interpreter through
314     # the env's PATH.  The problem with that is that it might not
315     # contain an ENV and a PATH.
316     if not cmd_interp:
317         systemroot = get_system_root()
318         tmp_path = systemroot + os.pathsep + \
319                    os.path.join(systemroot,'System32')
320         tmp_pathext = '.com;.exe;.bat;.cmd'
321         if os.environ.has_key('PATHEXT'):
322             tmp_pathext = os.environ['PATHEXT'] 
323         cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext)
324         if not cmd_interp:
325             cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext)
326
327     if not cmd_interp:
328         cmd_interp = env.Detect('cmd')
329         if not cmd_interp:
330             cmd_interp = env.Detect('command')
331
332     
333     if not env.has_key('ENV'):
334         env['ENV']        = {}
335
336     # Import things from the external environment to the construction
337     # environment's ENV.  This is a potential slippery slope, because we
338     # *don't* want to make builds dependent on the user's environment by
339     # default.  We're doing this for SystemRoot, though, because it's
340     # needed for anything that uses sockets, and seldom changes, and
341     # for SystemDrive because it's related.
342     #
343     # Weigh the impact carefully before adding other variables to this list.
344     import_env = [ 'SystemDrive', 'SystemRoot', 'TEMP', 'TMP' ]
345     for var in import_env:
346         v = os.environ.get(var)
347         if v:
348             env['ENV'][var] = v
349
350     if not env['ENV'].has_key('COMSPEC'):
351         v = os.environ.get("COMSPEC")
352         if v:
353             env['ENV']['COMSPEC'] = v
354
355     env.AppendENVPath('PATH', get_system_root() + '\System32')
356
357     env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD'
358     env['OBJPREFIX']      = ''
359     env['OBJSUFFIX']      = '.obj'
360     env['SHOBJPREFIX']    = '$OBJPREFIX'
361     env['SHOBJSUFFIX']    = '$OBJSUFFIX'
362     env['PROGPREFIX']     = ''
363     env['PROGSUFFIX']     = '.exe'
364     env['LIBPREFIX']      = ''
365     env['LIBSUFFIX']      = '.lib'
366     env['SHLIBPREFIX']    = ''
367     env['SHLIBSUFFIX']    = '.dll'
368     env['LIBPREFIXES']    = [ '$LIBPREFIX' ]
369     env['LIBSUFFIXES']    = [ '$LIBSUFFIX' ]
370     env['PSPAWN']         = piped_spawn
371     env['SPAWN']          = spawn
372     env['SHELL']          = cmd_interp
373     env['TEMPFILE']       = TempFileMunge
374     env['TEMPFILEPREFIX'] = '@'
375     env['MAXLINELENGTH']  = 2048
376     env['ESCAPE']         = escape
377     
378     env['HOST_OS']        = 'win32'
379     env['HOST_ARCH']      = get_architecture().arch
380     
381
382 # Local Variables:
383 # tab-width:4
384 # indent-tabs-mode:nil
385 # End:
386 # vim: set expandtab tabstop=4 shiftwidth=4: