Clone of official tools
Diff: toolchains/__init__.py
- Revision:
- 38:399953da035d
- Parent:
- 36:96847d42f010
- Child:
- 40:7d3fa6b99b2b
--- a/toolchains/__init__.py Fri Jul 07 16:20:25 2017 -0500 +++ b/toolchains/__init__.py Thu Jul 13 15:26:26 2017 -0500 @@ -22,7 +22,8 @@ from time import time, sleep from types import ListType from shutil import copyfile -from os.path import join, splitext, exists, relpath, dirname, basename, split, abspath, isfile, isdir +from os.path import join, splitext, exists, relpath, dirname, basename, split, abspath, isfile, isdir, normcase +from itertools import chain from inspect import getmro from copy import deepcopy from tools.config import Config @@ -42,6 +43,78 @@ CPU_COUNT_MIN = 1 CPU_COEF = 1 +class LazyDict(dict): + def __init__(self): + self.eager = {} + self.lazy = {} + + def add_lazy(self, key, thunk): + if key in self.eager: + del self.eager[key] + self.lazy[key] = thunk + + def __getitem__(self, key): + if (key not in self.eager + and key in self.lazy): + self.eager[key] = self.lazy[key]() + del self.lazy[key] + return self.eager[key] + + def __setitem__(self, key, value): + self.eager[key] = value + + def __delitem__(self, key): + if key in self.eager: + del self.eager[key] + else: + del self.lazy[key] + + def __contains__(self, key): + return key in self.eager or key in self.lazy + + def __iter__(self): + return chain(iter(self.eager), iter(self.lazy)) + + def __len__(self): + return len(self.eager) + len(self.lazy) + + def __str__(self): + return "Lazy{%s}" % ( + ", ".join("%r: %r" % (k, v) for k, v in + chain(self.eager.iteritems(), ((k, "not evaluated") + for k in self.lazy)))) + + def update(self, other): + if isinstance(other, LazyDict): + self.eager.update(other.eager) + self.lazy.update(other.lazy) + else: + self.eager.update(other) + + def iteritems(self): + """Warning: This forces the evaluation all of the items in this LazyDict + that are iterated over.""" + for k, v in self.eager.iteritems(): + yield k, v + for k in self.lazy.keys(): + yield k, self[k] + + def apply(self, fn): + """Delay the application of a computation to all items of the lazy dict. + Does no computation now. Instead the comuptation is performed when a + consumer attempts to access a value in this LazyDict""" + new_lazy = {} + for k, f in self.lazy.iteritems(): + def closure(f=f): + return fn(f()) + new_lazy[k] = closure + for k, v in self.eager.iteritems(): + def closure(v=v): + return fn(v) + new_lazy[k] = closure + self.lazy = new_lazy + self.eager = {} + class Resources: def __init__(self, base_path=None): self.base_path = base_path @@ -74,7 +147,7 @@ self.json_files = [] # Features - self.features = {} + self.features = LazyDict() def __add__(self, resources): if resources is None: @@ -165,7 +238,9 @@ v = [rel_path(f, base, dot) for f in getattr(self, field)] setattr(self, field, v) - self.features = {k: f.relative_to(base, dot) for k, f in self.features.iteritems() if f} + def to_apply(feature, base=base, dot=dot): + feature.relative_to(base, dot) + self.features.apply(to_apply) if self.linker_script is not None: self.linker_script = rel_path(self.linker_script, base, dot) @@ -178,7 +253,9 @@ v = [f.replace('\\', '/') for f in getattr(self, field)] setattr(self, field, v) - self.features = {k: f.win_to_unix() for k, f in self.features.iteritems() if f} + def to_apply(feature): + feature.win_to_unix() + self.features.apply(to_apply) if self.linker_script is not None: self.linker_script = self.linker_script.replace('\\', '/') @@ -215,7 +292,7 @@ # standard labels for the "TARGET_" and "TOOLCHAIN_" specific directories, but # had the knowledge of a list of these directories to be ignored. LEGACY_IGNORE_DIRS = set([ - 'LPC11U24', 'LPC1768', 'LPC4088', 'LPC812', 'KL25Z', + 'LPC11U24', 'LPC1768', 'LPC2368', 'LPC4088', 'LPC812', 'KL25Z', 'ARM', 'uARM', 'IAR', 'GCC_ARM', 'GCC_CS', 'GCC_CR', 'GCC_CW', 'GCC_CW_EWL', 'GCC_CW_NEWLIB', ]) @@ -306,6 +383,7 @@ # Ignore patterns from .mbedignore files self.ignore_patterns = [] + self._ignore_regex = re.compile("$^") # Pre-mbed 2.0 ignore dirs self.legacy_ignore_dirs = (LEGACY_IGNORE_DIRS | TOOLCHAINS) - set([target.name, LEGACY_TOOLCHAIN_NAMES[self.name]]) @@ -511,10 +589,7 @@ def is_ignored(self, file_path): """Check if file path is ignored by any .mbedignore thus far""" - for pattern in self.ignore_patterns: - if fnmatch.fnmatch(file_path, pattern): - return True - return False + return self._ignore_regex.match(normcase(file_path)) def add_ignore_patterns(self, root, base_path, patterns): """Add a series of patterns to the ignored paths @@ -526,9 +601,10 @@ """ real_base = relpath(root, base_path) if real_base == ".": - self.ignore_patterns.extend(patterns) + self.ignore_patterns.extend(normcase(p) for p in patterns) else: - self.ignore_patterns.extend(join(real_base, pat) for pat in patterns) + self.ignore_patterns.extend(normcase(join(real_base, pat)) for pat in patterns) + self._ignore_regex = re.compile("|".join(fnmatch.translate(p) for p in self.ignore_patterns)) # Create a Resources object from the path pointed to by *path* by either traversing a # a directory structure, when *path* is a directory, or adding *path* to the resources, @@ -604,7 +680,9 @@ elif d.startswith('FEATURE_'): # Recursively scan features but ignore them in the current scan. # These are dynamically added by the config system if the conditions are matched - resources.features[d[8:]] = self.scan_resources(dir_path, base_path=base_path) + def closure (dir_path=dir_path, base_path=base_path): + return self.scan_resources(dir_path, base_path=base_path) + resources.features.add_lazy(d[8:], closure) dirs.remove(d) elif exclude_paths: for exclude_path in exclude_paths: