1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/python/mozbuild/dumbmake/dumbmake.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,122 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +from __future__ import unicode_literals 1.9 + 1.10 +from collections import OrderedDict 1.11 +from itertools import groupby 1.12 +from operator import itemgetter 1.13 +from os.path import dirname 1.14 + 1.15 +WHITESPACE_CHARACTERS = ' \t' 1.16 + 1.17 +def indentation(line): 1.18 + """Number of whitespace (tab and space) characters at start of |line|.""" 1.19 + i = 0 1.20 + while i < len(line): 1.21 + if line[i] not in WHITESPACE_CHARACTERS: 1.22 + break 1.23 + i += 1 1.24 + return i 1.25 + 1.26 +def dependency_map(lines): 1.27 + """Return a dictionary with keys that are targets and values that 1.28 + are ordered lists of targets that should also be built. 1.29 + 1.30 + This implementation is O(n^2), but lovely and simple! We walk the 1.31 + targets in the list, and for each target we walk backwards 1.32 + collecting its dependencies. To make the walking easier, we 1.33 + reverse the list so that we are always walking forwards. 1.34 + 1.35 + """ 1.36 + pairs = [(indentation(line), line.strip()) for line in lines] 1.37 + pairs.reverse() 1.38 + 1.39 + deps = {} 1.40 + 1.41 + for i, (indent, target) in enumerate(pairs): 1.42 + if not deps.has_key(target): 1.43 + deps[target] = [] 1.44 + 1.45 + for j in range(i+1, len(pairs)): 1.46 + ind, tar = pairs[j] 1.47 + if ind < indent: 1.48 + indent = ind 1.49 + if tar not in deps[target]: 1.50 + deps[target].append(tar) 1.51 + 1.52 + return deps 1.53 + 1.54 +def all_dependencies(*targets, **kwargs): 1.55 + """Return a list containing all the dependencies of |targets|. 1.56 + 1.57 + The relative order of targets is maintained if possible. 1.58 + 1.59 + """ 1.60 + dm = kwargs.pop('dependency_map', None) 1.61 + if dm is None: 1.62 + dm = dependency_map(targets) 1.63 + 1.64 + all_targets = OrderedDict() # Used as an ordered set. 1.65 + 1.66 + for target in targets: 1.67 + if target in dm: 1.68 + for dependency in dm[target]: 1.69 + # Move element back in the ordered set. 1.70 + if dependency in all_targets: 1.71 + del all_targets[dependency] 1.72 + all_targets[dependency] = True 1.73 + 1.74 + return all_targets.keys() 1.75 + 1.76 +def get_components(path): 1.77 + """Take a path and return all the components of the path.""" 1.78 + paths = [path] 1.79 + while True: 1.80 + parent = dirname(paths[-1]) 1.81 + if parent == "": 1.82 + break 1.83 + paths.append(parent) 1.84 + 1.85 + paths.reverse() 1.86 + return paths 1.87 + 1.88 +def add_extra_dependencies(target_pairs, dependency_map): 1.89 + """Take a list [(make_dir, make_target)] and expand (make_dir, None) 1.90 + entries with extra make dependencies from |dependency_map|. 1.91 + 1.92 + Returns an iterator of pairs (make_dir, make_target). 1.93 + 1.94 + """ 1.95 + all_targets = OrderedDict() # Used as an ordered set. 1.96 + make_dirs = OrderedDict() # Used as an ordered set. 1.97 + 1.98 + for make_target, group in groupby(target_pairs, itemgetter(1)): 1.99 + # Return non-simple directory targets untouched. 1.100 + if make_target is not None: 1.101 + for pair in group: 1.102 + # Generate dependencies for all components of a path. 1.103 + # Given path a/b/c, examine a, a/b, and a/b/c in that order. 1.104 + paths = get_components(pair[1]) 1.105 + # For each component of a path, find and add all dependencies 1.106 + # to the final target list. 1.107 + for target in paths: 1.108 + if target not in all_targets: 1.109 + yield pair[0], target 1.110 + all_targets[target] = True 1.111 + continue 1.112 + 1.113 + # Add extra dumbmake dependencies to simple directory targets. 1.114 + for make_dir, _ in group: 1.115 + if make_dir not in make_dirs: 1.116 + yield make_dir, None 1.117 + make_dirs[make_dir] = True 1.118 + 1.119 + all_components = [] 1.120 + for make_dir in make_dirs.iterkeys(): 1.121 + all_components.extend(get_components(make_dir)) 1.122 + 1.123 + for i in all_dependencies(*all_components, dependency_map=dependency_map): 1.124 + if i not in make_dirs: 1.125 + yield i, None