3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """Platform-specific initialization for Win32 systems.
26 There normally shouldn't be any need to import this module directly. It
27 will usually be imported through the generic SCons.Platform.Platform()
37 from SCons
.Platform
.posix
import exitvalmap
38 from SCons
.Platform
import TempFileMunge
39 from SCons
.Platform
.virtualenv
import ImportVirtualenv
40 from SCons
.Platform
.virtualenv
import ignore_virtualenv
, enable_virtualenv
43 CHOCO_DEFAULT_PATH
= [
44 r
'C:\ProgramData\chocolatey\bin'
48 # Now swap out shutil.filecopy and filecopy2 for win32 api native CopyFile
50 from ctypes
import windll
53 CopyFile
= windll
.kernel32
.CopyFileA
54 SetFileTime
= windll
.kernel32
.SetFileTime
56 _shutil_copy
= shutil
.copy
57 _shutil_copy2
= shutil
.copy2
59 shutil
.copy2
= CopyFile
61 def win_api_copyfile(src
,dst
) -> None:
65 shutil
.copy
= win_api_copyfile
67 except AttributeError:
69 "Couldn't override shutil.copy or shutil.copy2 falling back to shutil defaults"
79 spawn_lock
= threading
.Lock()
81 # This locked version of spawnve works around a Windows
82 # MSVCRT bug, because its spawnve is not thread-safe.
83 # Without this, python can randomly crash while using -jN.
84 # See the python bug at https://github.com/python/cpython/issues/50725
85 # and SCons issue at https://github.com/SCons/scons/issues/2449
86 def spawnve(mode
, file, args
, env
):
90 ret
= os
.spawnve(os
.P_NOWAIT
, file, args
, env
)
92 ret
= os
.spawnve(mode
, file, args
, env
)
96 pid
, status
= os
.waitpid(ret
, 0)
100 # Use the unsafe method of spawnve.
101 # Please, don't try to optimize this try-except block
102 # away by assuming that the threading module is always present.
103 # In the test test/option-j.py we intentionally call SCons with
104 # a fake threading.py that raises an import exception right away,
105 # simulating a non-existent package.
106 def spawnve(mode
, file, args
, env
):
107 return os
.spawnve(mode
, file, args
, env
)
109 # The upshot of all this is that, if you are using Python 1.5.2,
110 # you had better have cmd or command.com in your PATH when you run
114 def piped_spawn(sh
, escape
, cmd
, args
, env
, stdout
, stderr
):
115 # There is no direct way to do that in python. What we do
116 # here should work for most cases:
117 # In case stdout (stderr) is not redirected to a file,
118 # we redirect it into a temporary file tmpFileStdout
119 # (tmpFileStderr) and copy the contents of this file
120 # to stdout (stderr) given in the argument
121 # Note that because this will paste shell redirection syntax
122 # into the cmdline, we have to call a shell to run the command,
123 # even though that's a bit of a performance hit.
125 sys
.stderr
.write("scons: Could not find command interpreter, is it in your PATH?\n")
128 # one temporary file for stdout and stderr
129 tmpFileStdout
, tmpFileStdoutName
= tempfile
.mkstemp(text
=True)
130 os
.close(tmpFileStdout
) # don't need open until the subproc is done
131 tmpFileStderr
, tmpFileStderrName
= tempfile
.mkstemp(text
=True)
132 os
.close(tmpFileStderr
)
134 # check if output is redirected
135 stdoutRedirected
= False
136 stderrRedirected
= False
138 # are there more possibilities to redirect stdout ?
139 if arg
.find(">", 0, 1) != -1 or arg
.find("1>", 0, 2) != -1:
140 stdoutRedirected
= True
141 # are there more possibilities to redirect stderr ?
142 if arg
.find("2>", 0, 2) != -1:
143 stderrRedirected
= True
145 # redirect output of non-redirected streams to our tempfiles
146 if not stdoutRedirected
:
147 args
.append(">" + tmpFileStdoutName
)
148 if not stderrRedirected
:
149 args
.append("2>" + tmpFileStderrName
)
151 # Sanitize encoding. None is not a valid encoding.
152 # Since we're handling a redirected shell command use
153 # the shells default encoding.
154 if stdout
.encoding
is None:
155 stdout
.encoding
= 'oem'
156 if stderr
.encoding
is None:
157 stderr
.encoding
= 'oem'
159 # actually do the spawn
161 args
= [sh
, '/C', escape(' '.join(args
))]
162 ret
= spawnve(os
.P_WAIT
, sh
, args
, env
)
166 ret
= exitvalmap
[e
.errno
]
168 sys
.stderr
.write("scons: unknown OSError exception code %d - %s: %s\n" % (e
.errno
, cmd
, e
.strerror
))
169 if stderr
is not None:
170 stderr
.write("scons: %s: %s\n" % (cmd
, e
.strerror
))
172 # copy child output from tempfiles to our streams
173 # and do clean up stuff
174 if stdout
is not None and not stdoutRedirected
:
176 with
open(tmpFileStdoutName
, "rb") as tmpFileStdout
:
177 output
= tmpFileStdout
.read()
178 stdout
.write(output
.decode(stdout
.encoding
, "replace"))
179 os
.remove(tmpFileStdoutName
)
183 if stderr
is not None and not stderrRedirected
:
185 with
open(tmpFileStderrName
, "rb") as tmpFileStderr
:
186 errors
= tmpFileStderr
.read()
187 stderr
.write(errors
.decode(stderr
.encoding
, "replace"))
188 os
.remove(tmpFileStderrName
)
195 def exec_spawn(l
, env
):
197 result
= spawnve(os
.P_WAIT
, l
[0], l
, env
)
200 result
= exitvalmap
[e
.errno
]
201 sys
.stderr
.write("scons: %s: %s\n" % (l
[0], e
.strerror
))
206 command
= ' '.join(l
[0:3])
211 sys
.stderr
.write("scons: unknown OSError exception code %d - '%s': %s\n" % (e
.errno
, command
, e
.strerror
))
215 def spawn(sh
, escape
, cmd
, args
, env
):
217 sys
.stderr
.write("scons: Could not find command interpreter, is it in your PATH?\n")
219 return exec_spawn([sh
, '/C', escape(' '.join(args
))], env
)
221 # Windows does not allow special characters in file names anyway, so no
222 # need for a complex escape function, we will just quote the arg, except
223 # that "cmd /c" requires that if an argument ends with a backslash it
224 # needs to be escaped so as not to interfere with closing double quote
231 # Get the windows system directory name
235 def get_system_root():
237 if _system_root
is not None:
240 # A resonable default if we can't read the registry
241 val
= os
.environ
.get('SystemRoot', "C:\\WINDOWS")
243 if SCons
.Util
.can_read_reg
:
245 # Look for Windows NT system root
246 k
=SCons
.Util
.RegOpenKeyEx(SCons
.Util
.hkey_mod
.HKEY_LOCAL_MACHINE
,
247 'Software\\Microsoft\\Windows NT\\CurrentVersion')
248 val
, tok
= SCons
.Util
.RegQueryValueEx(k
, 'SystemRoot')
249 except SCons
.Util
.RegError
:
251 # Okay, try the Windows 9x system root
252 k
=SCons
.Util
.RegOpenKeyEx(SCons
.Util
.hkey_mod
.HKEY_LOCAL_MACHINE
,
253 'Software\\Microsoft\\Windows\\CurrentVersion')
254 val
, tok
= SCons
.Util
.RegQueryValueEx(k
, 'SystemRoot')
255 except KeyboardInterrupt:
264 def get_program_files_dir():
266 Get the location of the program files directory
271 # Now see if we can look in the registry...
273 if SCons
.Util
.can_read_reg
:
275 # Look for Windows Program Files directory
276 k
=SCons
.Util
.RegOpenKeyEx(SCons
.Util
.hkey_mod
.HKEY_LOCAL_MACHINE
,
277 'Software\\Microsoft\\Windows\\CurrentVersion')
278 val
, tok
= SCons
.Util
.RegQueryValueEx(k
, 'ProgramFilesDir')
279 except SCons
.Util
.RegError
:
283 # A reasonable default if we can't read the registry
284 # (Actually, it's pretty reasonable even if we can :-)
285 val
= os
.path
.join(os
.path
.dirname(get_system_root()),"Program Files")
290 class ArchDefinition
:
292 Determine which windows CPU were running on.
293 A class for defining architecture-specific settings and logic.
295 def __init__(self
, arch
, synonyms
=[]) -> None:
297 self
.synonyms
= synonyms
299 SupportedArchitectureList
= [
302 ['i386', 'i486', 'i586', 'i686'],
307 ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'],
312 ['ARM64', 'aarch64', 'AARCH64', 'AArch64'],
321 SupportedArchitectureMap
= {}
322 for a
in SupportedArchitectureList
:
323 SupportedArchitectureMap
[a
.arch
] = a
325 SupportedArchitectureMap
[s
] = a
328 def get_architecture(arch
=None):
329 """Returns the definition for the specified architecture string.
331 If no string is specified, the system default is returned (as defined
332 by the registry PROCESSOR_ARCHITECTURE value, PROCESSOR_ARCHITEW6432
333 environment variable, PROCESSOR_ARCHITECTURE environment variable, or
334 the platform machine).
337 if SCons
.Util
.can_read_reg
:
339 k
=SCons
.Util
.RegOpenKeyEx(SCons
.Util
.hkey_mod
.HKEY_LOCAL_MACHINE
,
340 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment')
341 val
, tok
= SCons
.Util
.RegQueryValueEx(k
, 'PROCESSOR_ARCHITECTURE')
342 except SCons
.Util
.RegError
:
344 if val
and val
in SupportedArchitectureMap
:
347 arch
= os
.environ
.get('PROCESSOR_ARCHITEW6432')
349 arch
= os
.environ
.get('PROCESSOR_ARCHITECTURE')
350 return SupportedArchitectureMap
.get(arch
, ArchDefinition(platform
.machine(), [platform
.machine()]))
354 # Attempt to find cmd.exe (for WinNT/2k/XP) or
355 # command.com for Win9x
357 # First see if we can look in the registry...
358 if SCons
.Util
.can_read_reg
:
360 # Look for Windows NT system root
361 k
=SCons
.Util
.RegOpenKeyEx(SCons
.Util
.hkey_mod
.HKEY_LOCAL_MACHINE
,
362 'Software\\Microsoft\\Windows NT\\CurrentVersion')
363 val
, tok
= SCons
.Util
.RegQueryValueEx(k
, 'SystemRoot')
364 cmd_interp
= os
.path
.join(val
, 'System32\\cmd.exe')
365 except SCons
.Util
.RegError
:
367 # Okay, try the Windows 9x system root
368 k
=SCons
.Util
.RegOpenKeyEx(SCons
.Util
.hkey_mod
.HKEY_LOCAL_MACHINE
,
369 'Software\\Microsoft\\Windows\\CurrentVersion')
370 val
, tok
= SCons
.Util
.RegQueryValueEx(k
, 'SystemRoot')
371 cmd_interp
= os
.path
.join(val
, 'command.com')
372 except KeyboardInterrupt:
377 # For the special case of not having access to the registry, we
378 # use a temporary path and pathext to attempt to find the command
379 # interpreter. If we fail, we try to find the interpreter through
380 # the env's PATH. The problem with that is that it might not
381 # contain an ENV and a PATH.
383 systemroot
= get_system_root()
384 tmp_path
= systemroot
+ os
.pathsep
+ \
385 os
.path
.join(systemroot
,'System32')
386 tmp_pathext
= '.com;.exe;.bat;.cmd'
387 if 'PATHEXT' in os
.environ
:
388 tmp_pathext
= os
.environ
['PATHEXT']
389 cmd_interp
= SCons
.Util
.WhereIs('cmd', tmp_path
, tmp_pathext
)
391 cmd_interp
= SCons
.Util
.WhereIs('command', tmp_path
, tmp_pathext
)
394 cmd_interp
= env
.Detect('cmd')
396 cmd_interp
= env
.Detect('command')
401 # Import things from the external environment to the construction
402 # environment's ENV. This is a potential slippery slope, because we
403 # *don't* want to make builds dependent on the user's environment by
404 # default. We're doing this for SystemRoot, though, because it's
405 # needed for anything that uses sockets, and seldom changes, and
406 # for SystemDrive because it's related.
408 # Weigh the impact carefully before adding other variables to this list.
409 import_env
= ['SystemDrive', 'SystemRoot', 'TEMP', 'TMP', 'USERPROFILE']
410 for var
in import_env
:
411 v
= os
.environ
.get(var
)
415 if 'COMSPEC' not in env
['ENV']:
416 v
= os
.environ
.get("COMSPEC")
418 env
['ENV']['COMSPEC'] = v
420 env
.AppendENVPath('PATH', get_system_root() + '\\System32')
422 env
['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD'
423 env
['OBJPREFIX'] = ''
424 env
['OBJSUFFIX'] = '.obj'
425 env
['SHOBJPREFIX'] = '$OBJPREFIX'
426 env
['SHOBJSUFFIX'] = '$OBJSUFFIX'
427 env
['PROGPREFIX'] = ''
428 env
['PROGSUFFIX'] = '.exe'
429 env
['LIBPREFIX'] = ''
430 env
['LIBSUFFIX'] = '.lib'
431 env
['SHLIBPREFIX'] = ''
432 env
['SHLIBSUFFIX'] = '.dll'
433 env
['LIBPREFIXES'] = ['$LIBPREFIX']
434 env
['LIBSUFFIXES'] = ['$LIBSUFFIX']
435 env
['LIBLITERALPREFIX'] = ''
436 env
['PSPAWN'] = piped_spawn
438 env
['SHELL'] = cmd_interp
439 env
['TEMPFILE'] = TempFileMunge
440 env
['TEMPFILEPREFIX'] = '@'
441 env
['MAXLINELENGTH'] = 2048
442 env
['ESCAPE'] = escape
444 env
['HOST_OS'] = 'win32'
445 env
['HOST_ARCH'] = get_architecture().arch
447 if enable_virtualenv
and not ignore_virtualenv
:
448 ImportVirtualenv(env
)
453 # indent-tabs-mode:nil
455 # vim: set expandtab tabstop=4 shiftwidth=4: