You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

470 lines
16 KiB

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