waf/waflib/extras/win32_opts.py

171 lines
4.6 KiB
Python
Raw Normal View History

2012-04-02 01:11:21 +02:00
#! /usr/bin/env python
# encoding: utf-8
"""
Windows-specific optimizations
2016-05-29 16:58:41 +02:00
This module can help reducing the overhead of listing files on windows
(more than 10000 files). Python 3.5 already provides the listdir
optimization though.
2012-04-02 01:11:21 +02:00
"""
import os
from waflib import Utils, Build, Node, Logs
2012-04-02 01:11:21 +02:00
2012-06-15 19:02:32 +02:00
try:
TP = '%s\\*'.decode('ascii')
except AttributeError:
TP = '%s\\*'
2012-04-02 01:11:21 +02:00
if Utils.is_win32:
2016-07-25 19:05:36 +02:00
from waflib.Tools import md5_tstamp
2012-04-02 01:11:21 +02:00
import ctypes, ctypes.wintypes
FindFirstFile = ctypes.windll.kernel32.FindFirstFileW
FindNextFile = ctypes.windll.kernel32.FindNextFileW
FindClose = ctypes.windll.kernel32.FindClose
FILE_ATTRIBUTE_DIRECTORY = 0x10
INVALID_HANDLE_VALUE = -1
UPPER_FOLDERS = ('.', '..')
try:
UPPER_FOLDERS = [unicode(x) for x in UPPER_FOLDERS]
except NameError:
pass
def cached_hash_file(self):
try:
cache = self.ctx.cache_listdir_cache_hash_file
except AttributeError:
cache = self.ctx.cache_listdir_cache_hash_file = {}
if id(self.parent) in cache:
try:
t = cache[id(self.parent)][self.name]
except KeyError:
raise IOError('Not a file')
else:
# an opportunity to list the files and the timestamps at once
findData = ctypes.wintypes.WIN32_FIND_DATAW()
2012-06-15 19:02:32 +02:00
find = FindFirstFile(TP % self.parent.abspath(), ctypes.byref(findData))
2012-04-02 01:11:21 +02:00
if find == INVALID_HANDLE_VALUE:
cache[id(self.parent)] = {}
raise IOError('Not a file')
cache[id(self.parent)] = lst_files = {}
try:
while True:
if findData.cFileName not in UPPER_FOLDERS:
thatsadir = findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
if not thatsadir:
ts = findData.ftLastWriteTime
d = (ts.dwLowDateTime << 32) | ts.dwHighDateTime
lst_files[str(findData.cFileName)] = d
if not FindNextFile(find, ctypes.byref(findData)):
break
except Exception:
2012-04-02 01:11:21 +02:00
cache[id(self.parent)] = {}
raise IOError('Not a file')
finally:
FindClose(find)
t = lst_files[self.name]
2012-06-15 19:02:32 +02:00
fname = self.abspath()
if fname in Build.hashes_md5_tstamp:
if Build.hashes_md5_tstamp[fname][0] == t:
return Build.hashes_md5_tstamp[fname][1]
2012-04-02 01:11:21 +02:00
2012-06-15 19:02:32 +02:00
try:
fd = os.open(fname, os.O_BINARY | os.O_RDONLY | os.O_NOINHERIT)
except OSError:
raise IOError('Cannot read from %r' % fname)
f = os.fdopen(fd, 'rb')
m = Utils.md5()
rb = 1
try:
while rb:
rb = f.read(200000)
m.update(rb)
finally:
f.close()
2012-04-02 01:11:21 +02:00
# ensure that the cache is overwritten
2012-06-15 19:02:32 +02:00
Build.hashes_md5_tstamp[fname] = (t, m.digest())
2012-04-02 01:11:21 +02:00
return m.digest()
Node.Node.cached_hash_file = cached_hash_file
def get_bld_sig_win32(self):
try:
return self.ctx.hash_cache[id(self)]
except KeyError:
pass
except AttributeError:
self.ctx.hash_cache = {}
2015-12-23 17:50:48 +01:00
self.ctx.hash_cache[id(self)] = ret = Utils.h_file(self.abspath())
2012-04-02 01:11:21 +02:00
return ret
Node.Node.get_bld_sig = get_bld_sig_win32
def isfile_cached(self):
# optimize for nt.stat calls, assuming there are many files for few folders
try:
cache = self.__class__.cache_isfile_cache
except AttributeError:
cache = self.__class__.cache_isfile_cache = {}
try:
c1 = cache[id(self.parent)]
except KeyError:
c1 = cache[id(self.parent)] = []
curpath = self.parent.abspath()
findData = ctypes.wintypes.WIN32_FIND_DATAW()
2012-06-15 19:02:32 +02:00
find = FindFirstFile(TP % curpath, ctypes.byref(findData))
2012-04-02 01:11:21 +02:00
if find == INVALID_HANDLE_VALUE:
Logs.error("invalid win32 handle isfile_cached %r", self.abspath())
2012-04-02 01:11:21 +02:00
return os.path.isfile(self.abspath())
try:
while True:
if findData.cFileName not in UPPER_FOLDERS:
thatsadir = findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
if not thatsadir:
c1.append(str(findData.cFileName))
if not FindNextFile(find, ctypes.byref(findData)):
break
2012-06-15 19:02:32 +02:00
except Exception as e:
Logs.error('exception while listing a folder %r %r', self.abspath(), e)
2012-04-02 01:11:21 +02:00
return os.path.isfile(self.abspath())
finally:
FindClose(find)
return self.name in c1
Node.Node.isfile_cached = isfile_cached
def find_or_declare_win32(self, lst):
# assuming that "find_or_declare" is called before the build starts, remove the calls to os.path.isfile
if isinstance(lst, str):
lst = [x for x in Utils.split_path(lst) if x and x != '.']
2012-04-02 01:11:21 +02:00
2016-07-28 22:03:39 +02:00
node = self.get_bld().search_node(lst)
2012-04-02 01:11:21 +02:00
if node:
if not node.isfile_cached():
try:
node.parent.mkdir()
2014-04-20 02:29:27 +02:00
except OSError:
2012-04-02 01:11:21 +02:00
pass
return node
self = self.get_src()
node = self.find_node(lst)
if node:
if not node.isfile_cached():
try:
node.parent.mkdir()
2014-04-20 02:29:27 +02:00
except OSError:
2012-04-02 01:11:21 +02:00
pass
return node
node = self.get_bld().make_node(lst)
node.parent.mkdir()
return node
Node.Node.find_or_declare = find_or_declare_win32