python/mozbuild/dumbmake/dumbmake.py

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 from __future__ import unicode_literals
michael@0 6
michael@0 7 from collections import OrderedDict
michael@0 8 from itertools import groupby
michael@0 9 from operator import itemgetter
michael@0 10 from os.path import dirname
michael@0 11
michael@0 12 WHITESPACE_CHARACTERS = ' \t'
michael@0 13
michael@0 14 def indentation(line):
michael@0 15 """Number of whitespace (tab and space) characters at start of |line|."""
michael@0 16 i = 0
michael@0 17 while i < len(line):
michael@0 18 if line[i] not in WHITESPACE_CHARACTERS:
michael@0 19 break
michael@0 20 i += 1
michael@0 21 return i
michael@0 22
michael@0 23 def dependency_map(lines):
michael@0 24 """Return a dictionary with keys that are targets and values that
michael@0 25 are ordered lists of targets that should also be built.
michael@0 26
michael@0 27 This implementation is O(n^2), but lovely and simple! We walk the
michael@0 28 targets in the list, and for each target we walk backwards
michael@0 29 collecting its dependencies. To make the walking easier, we
michael@0 30 reverse the list so that we are always walking forwards.
michael@0 31
michael@0 32 """
michael@0 33 pairs = [(indentation(line), line.strip()) for line in lines]
michael@0 34 pairs.reverse()
michael@0 35
michael@0 36 deps = {}
michael@0 37
michael@0 38 for i, (indent, target) in enumerate(pairs):
michael@0 39 if not deps.has_key(target):
michael@0 40 deps[target] = []
michael@0 41
michael@0 42 for j in range(i+1, len(pairs)):
michael@0 43 ind, tar = pairs[j]
michael@0 44 if ind < indent:
michael@0 45 indent = ind
michael@0 46 if tar not in deps[target]:
michael@0 47 deps[target].append(tar)
michael@0 48
michael@0 49 return deps
michael@0 50
michael@0 51 def all_dependencies(*targets, **kwargs):
michael@0 52 """Return a list containing all the dependencies of |targets|.
michael@0 53
michael@0 54 The relative order of targets is maintained if possible.
michael@0 55
michael@0 56 """
michael@0 57 dm = kwargs.pop('dependency_map', None)
michael@0 58 if dm is None:
michael@0 59 dm = dependency_map(targets)
michael@0 60
michael@0 61 all_targets = OrderedDict() # Used as an ordered set.
michael@0 62
michael@0 63 for target in targets:
michael@0 64 if target in dm:
michael@0 65 for dependency in dm[target]:
michael@0 66 # Move element back in the ordered set.
michael@0 67 if dependency in all_targets:
michael@0 68 del all_targets[dependency]
michael@0 69 all_targets[dependency] = True
michael@0 70
michael@0 71 return all_targets.keys()
michael@0 72
michael@0 73 def get_components(path):
michael@0 74 """Take a path and return all the components of the path."""
michael@0 75 paths = [path]
michael@0 76 while True:
michael@0 77 parent = dirname(paths[-1])
michael@0 78 if parent == "":
michael@0 79 break
michael@0 80 paths.append(parent)
michael@0 81
michael@0 82 paths.reverse()
michael@0 83 return paths
michael@0 84
michael@0 85 def add_extra_dependencies(target_pairs, dependency_map):
michael@0 86 """Take a list [(make_dir, make_target)] and expand (make_dir, None)
michael@0 87 entries with extra make dependencies from |dependency_map|.
michael@0 88
michael@0 89 Returns an iterator of pairs (make_dir, make_target).
michael@0 90
michael@0 91 """
michael@0 92 all_targets = OrderedDict() # Used as an ordered set.
michael@0 93 make_dirs = OrderedDict() # Used as an ordered set.
michael@0 94
michael@0 95 for make_target, group in groupby(target_pairs, itemgetter(1)):
michael@0 96 # Return non-simple directory targets untouched.
michael@0 97 if make_target is not None:
michael@0 98 for pair in group:
michael@0 99 # Generate dependencies for all components of a path.
michael@0 100 # Given path a/b/c, examine a, a/b, and a/b/c in that order.
michael@0 101 paths = get_components(pair[1])
michael@0 102 # For each component of a path, find and add all dependencies
michael@0 103 # to the final target list.
michael@0 104 for target in paths:
michael@0 105 if target not in all_targets:
michael@0 106 yield pair[0], target
michael@0 107 all_targets[target] = True
michael@0 108 continue
michael@0 109
michael@0 110 # Add extra dumbmake dependencies to simple directory targets.
michael@0 111 for make_dir, _ in group:
michael@0 112 if make_dir not in make_dirs:
michael@0 113 yield make_dir, None
michael@0 114 make_dirs[make_dir] = True
michael@0 115
michael@0 116 all_components = []
michael@0 117 for make_dir in make_dirs.iterkeys():
michael@0 118 all_components.extend(get_components(make_dir))
michael@0 119
michael@0 120 for i in all_dependencies(*all_components, dependency_map=dependency_map):
michael@0 121 if i not in make_dirs:
michael@0 122 yield i, None

mercurial