jack2 codebase
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.

181 lines
4.8KB

  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. """
  4. Windows-specific optimizations
  5. This module can help reducing the overhead of listing files on windows (more than 10000 files).
  6. """
  7. import os
  8. try:
  9. import cPickle
  10. except ImportError:
  11. import pickle as cPickle
  12. from waflib import Utils, Build, Context, Node, Logs
  13. try:
  14. TP = '%s\\*'.decode('ascii')
  15. except AttributeError:
  16. TP = '%s\\*'
  17. if Utils.is_win32:
  18. from waflib.extras import md5_tstamp
  19. import ctypes, ctypes.wintypes
  20. FindFirstFile = ctypes.windll.kernel32.FindFirstFileW
  21. FindNextFile = ctypes.windll.kernel32.FindNextFileW
  22. FindClose = ctypes.windll.kernel32.FindClose
  23. FILE_ATTRIBUTE_DIRECTORY = 0x10
  24. INVALID_HANDLE_VALUE = -1
  25. UPPER_FOLDERS = ('.', '..')
  26. try:
  27. UPPER_FOLDERS = [unicode(x) for x in UPPER_FOLDERS]
  28. except NameError:
  29. pass
  30. def cached_hash_file(self):
  31. try:
  32. cache = self.ctx.cache_listdir_cache_hash_file
  33. except AttributeError:
  34. cache = self.ctx.cache_listdir_cache_hash_file = {}
  35. if id(self.parent) in cache:
  36. try:
  37. t = cache[id(self.parent)][self.name]
  38. except KeyError:
  39. raise IOError('Not a file')
  40. else:
  41. # an opportunity to list the files and the timestamps at once
  42. findData = ctypes.wintypes.WIN32_FIND_DATAW()
  43. find = FindFirstFile(TP % self.parent.abspath(), ctypes.byref(findData))
  44. if find == INVALID_HANDLE_VALUE:
  45. cache[id(self.parent)] = {}
  46. raise IOError('Not a file')
  47. cache[id(self.parent)] = lst_files = {}
  48. try:
  49. while True:
  50. if findData.cFileName not in UPPER_FOLDERS:
  51. thatsadir = findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
  52. if not thatsadir:
  53. ts = findData.ftLastWriteTime
  54. d = (ts.dwLowDateTime << 32) | ts.dwHighDateTime
  55. lst_files[str(findData.cFileName)] = d
  56. if not FindNextFile(find, ctypes.byref(findData)):
  57. break
  58. except Exception as e:
  59. cache[id(self.parent)] = {}
  60. raise IOError('Not a file')
  61. finally:
  62. FindClose(find)
  63. t = lst_files[self.name]
  64. fname = self.abspath()
  65. if fname in Build.hashes_md5_tstamp:
  66. if Build.hashes_md5_tstamp[fname][0] == t:
  67. return Build.hashes_md5_tstamp[fname][1]
  68. try:
  69. fd = os.open(fname, os.O_BINARY | os.O_RDONLY | os.O_NOINHERIT)
  70. except OSError:
  71. raise IOError('Cannot read from %r' % fname)
  72. f = os.fdopen(fd, 'rb')
  73. m = Utils.md5()
  74. rb = 1
  75. try:
  76. while rb:
  77. rb = f.read(200000)
  78. m.update(rb)
  79. finally:
  80. f.close()
  81. # ensure that the cache is overwritten
  82. Build.hashes_md5_tstamp[fname] = (t, m.digest())
  83. return m.digest()
  84. Node.Node.cached_hash_file = cached_hash_file
  85. def get_bld_sig_win32(self):
  86. try:
  87. return self.ctx.hash_cache[id(self)]
  88. except KeyError:
  89. pass
  90. except AttributeError:
  91. self.ctx.hash_cache = {}
  92. if not self.is_bld():
  93. if self.is_child_of(self.ctx.srcnode):
  94. self.sig = self.cached_hash_file()
  95. else:
  96. self.sig = Utils.h_file(self.abspath())
  97. self.ctx.hash_cache[id(self)] = ret = self.sig
  98. return ret
  99. Node.Node.get_bld_sig = get_bld_sig_win32
  100. def isfile_cached(self):
  101. # optimize for nt.stat calls, assuming there are many files for few folders
  102. try:
  103. cache = self.__class__.cache_isfile_cache
  104. except AttributeError:
  105. cache = self.__class__.cache_isfile_cache = {}
  106. try:
  107. c1 = cache[id(self.parent)]
  108. except KeyError:
  109. c1 = cache[id(self.parent)] = []
  110. curpath = self.parent.abspath()
  111. findData = ctypes.wintypes.WIN32_FIND_DATAW()
  112. find = FindFirstFile(TP % curpath, ctypes.byref(findData))
  113. if find == INVALID_HANDLE_VALUE:
  114. Logs.error("invalid win32 handle isfile_cached %r" % self.abspath())
  115. return os.path.isfile(self.abspath())
  116. try:
  117. while True:
  118. if findData.cFileName not in UPPER_FOLDERS:
  119. thatsadir = findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
  120. if not thatsadir:
  121. c1.append(str(findData.cFileName))
  122. if not FindNextFile(find, ctypes.byref(findData)):
  123. break
  124. except Exception as e:
  125. Logs.error('exception while listing a folder %r %r' % (self.abspath(), e))
  126. return os.path.isfile(self.abspath())
  127. finally:
  128. FindClose(find)
  129. return self.name in c1
  130. Node.Node.isfile_cached = isfile_cached
  131. def find_or_declare_win32(self, lst):
  132. # assuming that "find_or_declare" is called before the build starts, remove the calls to os.path.isfile
  133. if isinstance(lst, str):
  134. lst = [x for x in Node.split_path(lst) if x and x != '.']
  135. node = self.get_bld().search(lst)
  136. if node:
  137. if not node.isfile_cached():
  138. node.sig = None
  139. try:
  140. node.parent.mkdir()
  141. except OSError:
  142. pass
  143. return node
  144. self = self.get_src()
  145. node = self.find_node(lst)
  146. if node:
  147. if not node.isfile_cached():
  148. node.sig = None
  149. try:
  150. node.parent.mkdir()
  151. except OSError:
  152. pass
  153. return node
  154. node = self.get_bld().make_node(lst)
  155. node.parent.mkdir()
  156. return node
  157. Node.Node.find_or_declare = find_or_declare_win32