python/mozbuild/dumbmake/dumbmake.py

changeset 0
6474c204b198
     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

mercurial